Part of the DGfS PhD students’ forum, 23 February 2021

Instructors: Kyla McConnell and Julia Müller
Contact us on Twitter (@McconnellKyla, @JuliaMuellerFr)

Unless indicated, artwork is by the wonderful @allison_horst - find her on github.

(1) What is this “tidyverse”?

Let’s jump right in and load the package:

library(tidyverse)

The tidyverse is an extremely useful collection of R packages (i.e. add-ons to base-R) that help you get your data into a useful format (among other things).

The tidyverse

The following packages are included in the tidyverse:
- ggplot2: for data visualisation
- tibble: for tweaked dataframes
- tidyr: for data wrangling
- readr: for reading in files
- purrr: for functional progamming
- dplyr: for data manipulation
- stringr: for string manipulation
- forcats: for working with categorical variables (factors)

What is tidy data, and why do we use it?

Characteristics of tidy data:

What does tidy data look like?

Why this format?
- a lot of wrangling commands are based on the assumption that your data is tidy - is expected for many statistical models - works best for plotting - “Tidy datasets are all alike, but every messy dataset is messy in its own way” Hadley Wickham

Why use tidy data?

(2) Communicating with R

Understanding warnings and errors

R will often “talk” to you when you’re running code. For example, when you install a package, it’ll tell you e.g. where it is downloading a package from, and when it’s done. Similarly, when you loaded the tidyverse collection of packages, R listed them all. That’s nothing to worry about!

When there’s a mistake in the code (e.g. misspelling a variable name, forgetting to close quotation marks or brackets), R will give an error and be unable to run the line. The error message will give you important information about what went wrong.

hello <- "hi"
#Hello

In contrast, warnings are shown when R thinks there could be an issue with your code or input, but it still runs the line. R is generally just telling you that you MIGHT be making a logical error, not that the code is impossible.

c(1, 2, 3) + c(1, 2)

Reading function documentation

We’ll get to know a number of R functions today. These functions can take one or more arguments. As an example, let’s try out the say() function from the cowsay package.

First, (install and) load the cowsay package:

# install.packages("cowsay")
library(cowsay)

Try the following code:

say(
  what = "Good luck learning R!", 
  by = "rabbit")

We can see that this function has the what argument (what should be said?) and the by argument (which animal should say it?). But what other options are there for this command - which other animals, for example, or can you change the colour? To see the documentation for the say command, you can either run this line of code:

?say

…or type in say in the Help tab on the bottom right.

This will show you the documentation for the command.

  • Usage shows an overview of the function arguments and their defaults (e.g. if you typed in say() without any arguments in the brackets, you’d get the defaults, i.e. a cat saying “Hello world!”)
say()
  • Arguments provides more information on each argument. Arguments are the options you can use within a function.

  • what

  • by

  • type

  • what_color etc. Each of these can be fed the say() function to slightly alter what it does.

  • Examples at the bottom of the help page lists a few examples you can copy-paste into your code to better understand how a function works.

Don’t worry if you don’t understand everything in the documentation when you’re first starting out. Just try to get an idea for which arguments there are and which options for those arguments. It’s good practice to look at help documents often – this will also help you get more efficient at extracting the info you need from them.

(3) Reading in and exploring data

What type of file are you working with? Specifically, what’s used to separate the values in the different columns? To find out, open the file in a text editor. Common options are either commas or semicolons (in which cases, the file often has the ending .csv) or tabs (often .txt files).

Where is the file saved? If you are working in a Markdown and the data file is saved in the same location as the script file, you can use the name of the file with its ending, e.g. “new_data.csv” or “final_data.txt”. This is possible because RMarkdown automatically sets the working directory, i.e. the location R tries to find the file, to where it is saved. This is very convenient especially when you share your script and data. Your contributor doesn’t need to type in any long paths but can directly start working. If the file is saved in a subfolder called “data”, use “data/example_file.csv”). ../ allows you to go backwards one folder.

If you’re working in a script, use setwd() (e.g.: setwd(~Documents/PhD/Rscripts/...) or setwd(C:/Documents...)), or Session -> Set Working Directory -> Choose Directory…)

The commands to read in a file are: - base-R: read.csv() and read.tsv() - tidyverse (improvements, reads as tibble instead of dataframe): read_csv() and read_tsv() - must have tidyverse installed and loaded with a library() call - read_csv2() for semicolon-separated csv - read_delim(file, delim) for any other delimiters

Save the output to a variable using <-

So reading in data tends to follow this pattern:

name_of_data_in_R <- read_csv("data_file.csv") # equivalent to
name_of_data_in_R <- read_delim("data_file.csv", delim = ",")

name_of_data_in_R <- read_csv2("data_file.csv") # equivalent to
name_of_data_in_R <- read_delim("data_file.csv", delim = ";")

name_of_data_in_R <- read_tsv("data_file.txt") # tab-separated file

A note on the data we’ll use today

We’ve tried to tailor the workshop to be relevant to the kinds of data many of you said you used in the pre-workshop survey, so we’ll start with self-paced reading data (which looks fairly similar to eye-tracking data, another common response), and later use examples of corpus data and a questionnaire output. However, everything we discuss is useful for any kind of data! We’re also providing several files for you to practise on later, so you can pick data that looks closest to what you’re actually working with.

Let’s read in a small self-paced reading dataset (saved in the data folder, so we need to add data/ to tell R that):

spr <- read_csv("data/dgfs_spr.csv")
Parsed with column specification:
cols(
  X1 = col_double(),
  participant = col_character(),
  item_type = col_character(),
  sentence_num = col_double(),
  cond = col_character(),
  word = col_character(),
  RT = col_double(),
  full_sentence = col_character(),
  word_num = col_double()
)

The current dataframe is a self-paced reading experiment where 12 participants read 20 sentences each, plus 3 practice sentences to get them warmed up. Half the sentences were about dogs and half the sentences were about cats. In one condition (A), all sentences were paired with appropriate adjectival collocates according to the BNC (lap dog vs. tortoiseshell cat), in the other, these were reverse (lap cat vs. tortoiseshell dog). All sentences were presented in otherwise natural-sounding sentences.

Exploring our data

Now you have a data file read in, but how do you see what’s in it?

head(spr)

You can change the amount of rows with the n argument:

head(spr, n=3)

Or: click name of dataframe in Environment tab - can also sort columns and Filter rows – just for viewing purposes - bit slow if you start having huge dataframes but often a good first look

There’s also an easy way to see what the columns are:

colnames(spr)

summary(): call it on a dataframe to get each column and useful info based on the data type. For example, numeric columns will show the min, median, max and the quartiles (25% increments).

summary(spr)

summary(spr$RT)

Exercise

Read in and explore the example data How many rows and columns does it have?

Character vs. factor columns

In the environment panel (or using str()), you can see that all the variables in this data are read in as either numeric or character data. However, some variables should be treated as factors because they represent categories, not text data. Let’s convert them.

This way, the summary output also works as expected. For example, we can have a look at how many different participants and conditions:

summary(spr$participant)

summary(spr$cond)

If any other columns in your dataframe are read in wrong (for example, if you have a numeric column that looks like: “43”, “18” and is being read as a character column) you can convert them with similar syntax: as.numeric(), as.character() etc.

(4) The pipe %>%

One of the most noticeable features of the tidyverse is the pipe %>% (keyboard shortcut: Ctr/Cmd + Shift + M) .

The pipe takes the item before it and feeds it to the following command as the first argument. Since all tidyverse (and some non-tidyverse) functions take the dataframe as the first argument, this can be used to string together multiple functions.

So to re-write the head() function with the pipe:

spr %>% 
  head()

This produces the exact same output as head(spr). Why would this be useful?

You can see that the version with the pipe is easier to read when more than one function is called on the same dataframe!

Here are some more examples:

# Equivalent to summary(spr)
spr %>% 
  summary()

# Equivalent to colnames(spr), which returns all column names
spr %>% 
  colnames() 

# Equivalent to nrow(spr), which returns the number of rows in the df
spr %>% 
  nrow()

And you can also stack commands by ending each row (except the last one) with a pipe:

spr %>% 
  colnames() %>% #extracts column names
  nchar() #counts the number of letters

(5) Renaming and rearranging data

rename()

You can rename columns with the rename() function. The syntax is new_name = old_name. Let’s rename the cond variable:

spr %>% 
  rename(condition = cond)

This is just a preview because we didn’t assign the changed dataframe to any name. This is useful for testing code and making sure it does what you expect and want it to do.

  • if you look at the spr dataframe, for example in the Environment panel on the upper-right, the dataframe hasn’t changed

  • To save your changes: assigning your call back to the variable name

  • Good work flow: Preview then save when you’re sure you’re happy with the output

You can also rename multiple columns at once (no need for an array here):

Notice above that I’ve saved output over the spr dataframe to make the changes ‘permanent’.

There is no output when you simply save the response, but the spr dataframe has been permanently updated (within the R session, not in your file system)

If you make a mistake: arrow with a line under it in the code block of R-Markdown, runs all blocks above (but not the current one)

arrange()

There’s also a corresponding command that lets you sort by row values: arrange(). By default, this sorts by lowest to highest value, but you can add desc() to reverse that.

spr %>% 
  arrange(RT)

spr %>% 
  arrange(desc(RT))

(6) Subsets

select()

The traditional syntax for dealing with columns is dataframe\(column. A useful step in using pipes and tidyverse calls is the ability to *select* specific columns. That is, instead of writing `spr\)RT` we can write:

spr %>% 
  select(RT) 

Select multiple columns

You can also use select() to take multiple columns.

spr %>% 
  select(participant, word, RT)

You can see that these columns are presented in the order you gave them to the select call, too:

spr %>% 
  select(RT, word, participant)

You can also use this to reorder columns in that you give the name of the column(s) you want first, then finish with everything() to have all other columns follow:

spr %>% 
  select(RT, everything())

Preview vs. saving

Above, we’re mostly previewing using select() for first insights. If you look at the spr dataframe, for example in the Environment panel on the upper-right, the dataframe hasn’t changed. To save your changes, assign your call back to the variable name, i.e. df <- df %>% some operations here

Remove columns with select

You can also remove columns using select if you use the minus sign. For example, the item_type column is a factor with only one level - it always says “DashedSentence”. So let’s get rid of it:

spr %>% 
  select(-item_type)

You can also remove multiple columns at once by writing them in an array c(). We’d like to remove the item type column and also the first column (X1) which seems to be just a counter.

This overwrites the data as it is saved in R. It does not overwrite the file that is saved on your computer.

Leveling up select()

Until now, we’ve used select() in combination with the full column name, but there are helper functions that let you select columns based on other criteria.

For example, here’s how we can select both the sentence_num and the word_num column - by specifying ends_with("_num") in the select() call:

spr %>% 
  select(ends_with("_num"))

The opposite is also possible using starts_with()

contains is another helper function. Here, we’re using it to show all columns that contain an underscore:

spr %>% 
  select(contains("_"))

We can also select a range of variables using a colon. This works both with variables and (a range of) numbers:

spr %>% 
  select(condition:word) # shows condition, word_num, word

spr %>% 
  select(1:3) # first three columns

Here, the order of the columns matters!

Other helper functions are: - matches: similar to contains, but can use regular expressions - num_range: in a dataset with the variables X1, X2, X3, X4, Y1, and Y2, select(num_range(“X”, 1:3)) returns X1, X2, and X3

filter()

Filter based on a condition

While with select(), you can pick columns by name or if they fulfill conditions, filter() lets you look for rows that fulfill certain conditions.

Filter

Use filter() to return all items that fit a certain condition. For example, you can use: - equals to: == - not equal to: != - greater than: > - greater than or equal to: >= - less than: < - less than or equal to: <= - in (i.e. in a vector): %in%

Syntax: filter(data, columnname logical-operator condition) or, using the pipe: data %>% filter(columnname logical-operator condition)

Let’s look at reaction times that are shorter than 200 ms:

spr %>% 
  filter(RT < 200)

…reaction times longer than or equal to 250 ms:

spr %>% 
  filter(RT >= 250)

Or you can use it to select all items in a given category. Notice here that you have to use quotation marks to show you’re matching a character string. Look at the error below:

spr %>% 
  filter(word == relative)

The correct syntax is: (because you’re matching to a string)

spr %>% 
  filter(word == "relative")

You can also use filter to easily drop rows. Let’s drop all practice rows and save the output.

To use %in%, give an array of options (formatted in the correct way based on whether the column is a character or numeric):

spr %>% 
  filter(word %in% c("cat", "dog"))

spr %>% 
  filter(sentence_num %in% c(2, 4))

Note that filter is case-sensitive, so capitalization matters.

We can also specify several conditions in one filter() call, e.g. 

spr %>% 
  filter(word == "relative" & RT > 200)

spr %>% 
  filter(RT > 300 | RT < 150)

We can also “chain” different functions, which is one of the things that makes the pipe so useful. For example, we could filter for data in condition B, which is an incomplete sentence, and only look at the words and their response times:

spr %>% 
  filter(condition == "cond_B_dog") %>% 
  select(word, RT)

One useful function that can be chained to filter() is distinct(), which will return only the unique rows. Without an argument, it returns all rows that are unique in all columns.

You can also add a column name as an argument to return only the unique values in a certain column (useful with factors)

spr %>% 
  filter(condition == "cond_B_dog") %>% 
  distinct(sentence)

You can also use it on its own to return unique values or combinations of values.

spr %>% 
  distinct(sentence, condition)

(7) Separating and uniting columns

For the next two examples, we’ll use a different dataset called animal_corpus. Read it in and familiarise yourself with its contents.

animal_corpus <- read_csv2("data/cat_dog_corpus_data.csv")

This contains corpus data of “cat” and “dog” together with the words that precede “cat” and “dog”. These are called “collocates” and, in our example, also include part of speech tags (the format is word_tag).

This data is not tidy. Why?

The collocates column contains two variables (word and tag) although according to tidy data principles, each variable should be saved in its own column.

separate()

Luckily, the tidyverse has a command for that: separate(). It takes the following arguments: - data: our dataframe, we’ll pipe it - col: which column needs to be separated - into: a vector that contains the names of the new columns - sep: which symbol separates the values - remove (optional): by default, the original column will be deleted. Set remove to FALSE to keep it.

Another example: In the SPR data, the condition column contains two pieces of information: which condition the item was in (condA or condB) and which animal was being read about (cat or dog)

unite()

The opposite of separate(). This lets you glue columns together. - col is the name of the new column - the next argument, a vector, lists the columns that should be united - sep, as above, lets you specify how the values should be separated

Let’s say we’d like our data to be in the format “collocate cat/dog”, so without the tag, but in one column, separated by a space.

This leads to a lot of repetition - some bigrams appear several times in just our preview. To remedy this, we can use distinct(), which only keeps unique rows. To make clear that we want unique collocate cat/dog combinations, we can put coll_word into distinct() to make clear that this is the relevant column and tags should be ignored.

(8) Creating and changing columns with mutate()

With the mutate() function, you can change existing columns and add new ones. The syntax is: mutate(data, col_name = some_operation) or, with the pipe: data %>% mutate(col_name = some_operation).

Mutate

The response times are measured in ms. Let’s convert them to seconds by dividing by 1000:

(spr <- spr %>% 
  mutate(RT_s = RT / 1000))

Now, there’s a new column called RT_s (it’s at the very end by default).

You can also save the new column with the same name, and this will update all the items in that column (see below, where I divide response times by 1000, but note that I don’t save the output):

spr %>% 
  mutate(RT = RT / 1000)

You can also do operations to character columns - for example:

spr %>% 
  mutate(word = tolower(word))

Change data type in a column

We can also change data types using mutate(). Instead of the code we used earlier to convert participant and condition to factors, we could write:

spr <- spr %>% 
  mutate(participant = as_factor(participant),
         item_type = as_factor(item_type),
         condition = as_factor(condition))
Fehler: Problem with `mutate()` input `item_type`.
x Objekt 'item_type' nicht gefunden
i Input `item_type` is `as_factor(item_type)`.

As you can see, we can change several variables within one mutate() call. In the same way, we could create several new columns at the same time. Here, dropping each new column onto a new line is considered good style and makes the code more readable (but it’s not necessary).

Relabel factors

In our SPR experiment, condition A represents a match (i.e. cat/dog is presented with a matching collocate: purring cat, guide dog) and condition B is a mismatch (e.g. guide cat, purring dog). To make this clear in the data, we should label this explicitly. Within a mutate() command, we can use recode() to change the factor labels. The format for this is old label = “new label”.

Let’s look at our third dataset, animal_survey. It contains data from the same participants who answered a few sociodemographic questions and also indicated how cute they think animals are, how much they(’d) like look at, pet, and own an animal (scale: 1-5). Besides that, they were also asked to rate cats and dogs (scale: 1-7).

animal_survey <- read_csv("animal_survey.csv")

In our animal_survey data, education is represented by numeric codes, 1-4. We could turn these into labels like so:

Because these numbers should be treated as characters, we need to put them in quotation marks!

If-else-statements

Now for something fancy. You can also make new columns based on “if” conditions using the call ifelse(). The syntax of ifelse is: ifelse(this_is_true, this_happens, else_this_happens). For example, we could create a column called “RT_short” that contains “short” if the response time is faster than 100 ms and “long” if it isn’t:

spr %>% 
  mutate(RT_short = ifelse(RT < 100, "short", "long")) %>% 
  select(RT, RT_short)

You can also use ifelse on categorical / character columns:

Several conditions: case_when()

What if you have several conditions? For example, if RTs are shorter than 100 ms, they should be labelled “short”, if they’re longer than 500 ms, “long”, and “normal” for all other RTs. While it’s possible to chain several if_else() statements, it gets confusing and hard to read. Instead, we should use case_when().

Case when: an extension of if-else

The syntax within case_when() is: condition ~ what to do if it is true (can be used as often as you want and can even refer to different variables!), TRUE ~ what to do in all other cases.

spr %>% 
  mutate(RT_category = case_when(
    RT < 100 ~ "short",
    RT > 500 ~ "long",
    TRUE ~ "normal"
  )) %>% 
  select(RT, RT_category)

Another example: In the questionnaire, participants were asked to indicate how much they like cats and dogs, respectively. We can convert this information into a category. To create a variable called “preference”, we can use the following case_when() statement:

If we look at the output, we see that the “else/TRUE” condition wasn’t necessary, strictly speaking, but it’s good to account for all the potential cases in the data so you don’t end up with superfluous NAs.

(9) Summary tables with groupings

summarize()

Let’s extract some information on the reaction times in the self-paced reading data. If we want to do this the tidy way, we can use summarise(operation(variable)).

You can use multiple different operations in the summarize part, including: - mean(col_name) - median(col_name) - max(col_name) - min(col_name)

So to get the average RTs:

…and the median RTs:

spr %>% 
  summarise(median(RT))

By default, the column is labelled “mean(RT)” or “median(RT)”, respectively. We can set our own names, though:

spr %>% 
  summarise(average_RT = mean(RT))

group_by()

In our SPR experiment, we showed matching and mismatching collocate + cat/dog combinations, so we might assume that the mismatched combinations were read more slowly. We’d like to see the average RTs for each condition.

To look at summary statistics for specific groupings like these, we have to use a two- (more like three-) step process.

First, group by your grouping variable (here: condition) using group_by() Then, summarize, which creates a column based on a transformation to another column, using summarize() or summarise() Finally, ungroup (so that R forgets that this is a grouping and carries on as normal, with ungroup() Usually this makes no difference to what you see but is an important fail-safe if you’re continuing in the code block. For example, if you need to drop the former grouping variable and don’t use ungroup(), R will refuse to drop it.

Group and ungroup

For example, to return the average RT in each condition:

spr %>% 
  group_by(condition) %>% 
  summarize(mean(RT)) %>% 
  ungroup()

You can also give a name to your new summary column:

spr %>% 
  group_by(condition) %>% 
  summarize(average_RT = mean(RT)) %>% 
  ungroup()

Or further manipulate it, e.g. converting ms to seconds:

spr %>% 
  group_by(condition) %>% 
  summarize(average_RT_s = mean(RT) / 1000) %>% 
  ungroup()

count()

For categorical columns, you can also count how many rows are in each category using count() instead of summarize(). Here, we’re counting how often which word appeared in the experiment. The count is displayed in the new “n” column:

spr %>% 
  group_by(word) %>% 
  count() %>% 
  ungroup()

This is sorted alphabetically, but we can pipe it into the arrange() call we discussed earlier to sort by frequency, in descending order.

spr %>% 
  group_by(word) %>% 
  count() %>% 
  ungroup() %>% 
  arrange(desc(n))

You can also group by more than one column to return the unique combinations of the variables.

spr %>% 
  group_by(condition, animal) %>% 
  summarize(mean(RT))

(10) across()

across() is a helper for mutate() and summarise. It lets you easily apply a change or create a summary for several variables because you can use select() semantics (starts_with(), ends_with(), contains(), etc.) with across().

For example, if we’d like to see the mean for columns that end with “num”:

spr %>% 
  summarise(across(ends_with("num"), mean))

This also works with a list of functions, e.g. the mean and the standard deviation:

spr %>% 
  summarise(across(ends_with("num"), c(mean, sd)))

Here’s an example of across() in a mutate() function. This lets you easily convert all character variables into factors:

spr %>% 
  mutate(across(where(is.character), as.factor))

Row-wise operations

Back to our questionnaire. For each participant, we’d like to calculate the average of the columns that start with “animals” to get one measure of their interest in animals. We might try something like this:

animal_survey %>% 
  summarise(across(starts_with("animals"), mean))

As you can see, R gives us the averages for each of the columns, across all participants - not what we’re looking for! The reason for this is that R is best at computing over columns. To solve our problem, we need to add rowwise(). This is similar to the grouping idea (group_by()) we came across a little earlier. Here, we’re telling R to treat every row as a single “group”. We can then use c_across(), which is the version of across that works with row-wise operations. We also need mutate() because we want to create a new column rather than see a summary:

(11) Reshaping data

Now, let’s look into how to change the shape of your data. There are two options here: - making your data “longer”, i.e. increase the number of rows and decrease the number of columns - useful for tidying data, especially common with “wild-caught” data - command: pivot_longer() (older command: gather()) - making your data “wider”, i.e. decrease the number of rows and increase the number of columns - not (as) common for tidying but for creating summary tables - command: pivot_wider() (older command: spread())

Pivot

pivot_longer()

Let’s take another look at the questionnaire data.The format it is in is typical for questionnaire data as you would export it from host sites. The data from each person is represented on one line. This can be useful for some operations (like in the case_when() we used to create the “preference” variable) but a problem for other applications, so let’s tidy this using pivot_longer. Specifically, we have an issue with the dog and cat ratings being in separate columns. Instead, we should have a column called “animal”, which contains either “cats” or “dogs”, and another column that contains the rating.

The basic pivot_longer arguments are: cols: which columns should be reshaped? names_to: the name of the variable that the original column names (“cats” and “dogs”) should be stored in as values values_to: the name of the variable that the contents of the variables should be stored in

animal_survey %>% 
  pivot_longer(cols = c("cats", "dogs"),
               names_to = "animal",
               values_to = "rating")

Another example: This next file contains the averages of acceptability judgements for the sentences we used in the experiment (we’d expect that sentences which contain mismatching collocates such as “barking cat” are rated as less acceptable than sentences with matching collocates). Let’s read it in:

acc_judge_original <- read_csv("data/acceptability_judgements.csv")

We can see that this data is in a very wide format - each column name contains one of the sentences, and the only row consists of the average acceptability judgements.

What we’d like instead is a column that contains all the sentences and another column that contains all the averages.

(acc_judge <- acc_judge_original %>% 
  pivot_longer(
    cols = everything(), # we want to reshape the entire data, so all variables are concerned
    names_to = "sentence",
    values_to = "rating"
  ))

pivot_wider()

While pivot_longer() is often used to tidy data, its opposite pivot_wider() is more common when creating and reformatting summary tables. For example, let’s first count how often “cat” and “dog” occur with each collocate:

Looks like it’s working! However, we’d like to reshape this data into a more typical contingency table-like format, with “cat” and “dog” as row labels, and the collocates as column labels. We can use pivot_wider() to achieve this. Its main arguments are: names_from: where should the new column names come from? values_from: where should the corresponding values come from?

animal_corpus %>% 
  group_by(animal, coll) %>% 
  count() %>% 
  ungroup() %>% 
  pivot_wider(
    names_from = coll,
    values_from = n
  )

(12) Joining several data sets

Let’s add the acceptability judgement data to the SPR data: we want to “join” the two datasets into one. There are several join commands, which differ in how they match up datasets and which cases are kept or discarded.

(13) Writing files to disk

To write files to disk, you also need to select which separator you want to use – do you want a comma-separated file or a tab-separated file? In theory, this shouldn’t matter much, you just have to know which you pick.

CSVs are often saved as .csv (this is also openable in Excel) and TSVs are often saved as .txt

You can save CSVs and TSVs like so:

# write_tsv(spr, "spr_df.txt")

# write_csv(spr, "spr_df.csv")

Syntax: write_tsv(dfname, “filename.ext”)

Remember, something like data type in R (ex: character or factor) can’t be saved into one of these files – it just saves the values separated by the separator (and the column names). So you’ll still have to do the conversions next time you read the file into R.

(14) Intro to ggplot basics

ggplot2 ### The layered grammar of graphics ggplot follows the grammar of graphics. It starts with a blank graph that is fed a specific dataset, i.e. ggplot(data=DATASET). At this point, the graph doesn’t have any idea of which of the many variables in the dataset you’d like to use, which will go on the x- or y- axis or what type of graph you’re trying to produce.

You can see, at this stage we have, well… nothing!

ggplot(data = animal_data_complete)

Then, you add layers to this graph. These layers include an aesthetic mapping which defines how data is matched to the graphics (e.g. which variable is shown on the x-axis, which one is shown on the y-axis). Similarly, you can change the shape, size, and color of some or all points/lines/bars.

ggplot uses + to continue a line of code; this has to come at the END of the previous line, not at the beginning of the next line. These line breaks are optional, but make it a lot easier to read and understand the code.

When we tell ggplot how we want the aesthetics mapped, it can already start to set up the axes. However, it doesn’t yet know what type of graph we’re building:

ggplot(data = animal_data_complete) + 
  aes(x = bill_length_mm, y = bill_depth_mm) 

Finally, we add a geom which defines the type of plot we’re making. This may be a bar chart, a line graph, a scatterplot, or many more options we’ll explore below!

As a first example, let’s just put a point or dot on each data point using the axes above:

Other geoms

For one variable

Let’s look at the number of penguins per species. A bar plot is useful for this. The aes() only needs an argument for the x-axis, i.e. the category it should visualise. R then helpfully counts how many data points there are for each category, i.e. how many penguins per species.

To show the distribution of one continuous variable, a histogram is useful. Histograms take a continuous variable and divide it into bins. These bins divide the data into equally sized containers or ranges, and then show how many data points fall inside of these ranges. This shows you if the data comes from a smooth range or if more are coming from a certain part of the distribution.

Like bar plots, you only need to specify one variable and R does the counting for you.

You also see that R throws a warning about the bin size. By default, R will try to pick a reasonable amount of bins to check your data on. However, you can change this and see how it changes your idea of the distribution. Change this with the argument bins= inside of the geom_histogram()

Density plots are a variation of histograms where a smooth line instead of separate bars is drawn. This can be useful for a larger number of data points but can also hide some observations, so for this data, wouldn’t be advisable.

For two variables

For two continuous variables, we can use a scatterplot with geom_point() to show one dot per data point, defined against two continuous axes, like we saw in the example.

ggplot(data = penguins) +
  aes(x = bill_length_mm, y = bill_depth_mm) +
  geom_point() 

We can also use a (smoothed) line to represent the same data:

ggplot(data = penguins) +
  aes(x = bill_length_mm, y = bill_depth_mm) +
  geom_smooth() 

Here comes the cool part about ggplot.. you can also do both at the same time! Simply add both geoms to the same plot:

ggplot(data = penguins) +
  aes(x = body_mass_g, y = bill_length_mm) +
  geom_point() +
  geom_smooth()

In the boxplot below, the thick line in the middle is the median, the bottom of the box is the 1st Quartile and the lines reach up to the minimum and maximum values (shown as dots if they are extreme, i.e. they are outside of 1.5x the range from 1st to 3rd quartile: the interquartile range).

In this way, boxplots show you where the data is located, and how this differs between groups.

ggplot(data = penguins) +
  aes(x = species, y = bill_depth_mm) +
  geom_boxplot()
ggplot(data = penguins) +
  aes(x = species, y = bill_depth_mm) +
  geom_violin() 
ggplot(data = penguins) +
  aes(x = species, y = bill_depth_mm) +
  geom_violin() + 
  geom_boxplot() #try adding alpha = 0.5

Adding more information

Fill vs. color

ggplot distinguishes between fill= and color=. Both have to do with colors, but the difference is pretty intuitive. Fill is used for filling in larger spaced like entire bars. Color is used for smaller sections like dots or lines. In general, if your fill/color argument doesn’t work, try switching it to the other option and see if that fixes it!

colour

Let’s return to one of the first plots we made. Here, we can add a color argument to the geom call that calls a normal color (in quotes). This will make all the points this color. Note: if you know what hex codes are (or just use a hex code finder online), you can use all sorts of custom colors.

ggplot(data = penguins) +
  aes(x = bill_length_mm, y = bill_depth_mm) +
  geom_point(color = "blue") 

However, we can also use the color argument to introduce a third variable into the picture. If we are calling a variable/column, we do not need quotes AND we need to put it in the aes part of the call, becuase it is a -mapping- not a simple color.

ggplot(data = penguins) +
  aes(x = bill_length_mm, y = bill_depth_mm, color = species) +
  geom_point() 

Here’s what happens if you try to put a plain color inside of the aes mapping… R doesn’t find this variable, so it just randomly creates it..

ggplot(data = penguins) +
  aes(x = bill_length_mm, y = bill_depth_mm, color = "blue") +
  geom_point() 

The same idea works regardless of the type of plot, as long as it is one that takes color and not fill:

ggplot(data = penguins) +
  aes(x = bill_length_mm, y = bill_depth_mm, color = species) +
  geom_smooth()

So you see that adding a mapping to color (that is related to a categorical variable) always makes the assigned groups visible in the plot. In a way, it’s like adding a third variable to the plot.

The example above showed a categorical/discrete variable applied to color. However, you can also do it with a numeric variable, in which case the colors will range from light to dark blue.

ggplot(data = penguins) +
  aes(x = bill_length_mm, y = bill_depth_mm, color = body_mass_g) +
  geom_point() 

fill

Bar charts and other charts with pretty large open spaces usually need fill. Fill works the same as color in that it can either go inside aes() as a mapping or inside the geom, with quotes, as a plain color.

ggplot(data = penguins) +
  aes(x = species) +
  geom_bar(fill = "pink")

Here, we can use the variable “species” to color code the bars by species. This is a mapping to a variable, but it is the same variable that the x-axis is showing:

ggplot(data = penguins) +
  aes(x = species, fill = species) +
  geom_bar()

Similarly to color, you can also use fill to add an additional variable to your plot. This will change the type of the plot to a stacked bar chart, which can handle the additional variable.

ggplot(data = penguins) +
  aes(x = species, fill= sex) +
  geom_bar()

Bar plots can actually also take color, but this affects the color of the lines only:

ggplot(data = penguins) +
  aes(x = species, color= species) +
  geom_bar()

(15) Getting help

We hope to have provided you with a lot of tools and knowledge today that will help you work with your data! However, it obviously isn’t possible to cover all tidyverse functions that might be useful for your own data cleaning and analysis. Similarly, this often isn’t as smooth a process as we’ve presented here today. Sometimes, you’re not sure which commands to use, or which arguments a function has, or even which format your data actually needs to be in! This can be a frustrating experience, so we wanted to share some suggestions for how to re-frame negative thoughts and turn them into more positive and encouraging ones:

Hadley Wickham on how to deal with frustrating problems, from his keynote at the RStudio conference 2021

Function documentations & vignettes

As we showed you earlier, the function documentation (access via ?function or by typing the function into the “Help” panel on the bottom right) is really useful, but don’t expect to understand every single detail (that’s usually not necessary anyway for figuring out how to make it work or if you’re mostly checking how to spell an argument, etc.). If you’re trying to learn how to use a new function or remind yourself of functions you haven’t used in a while and are finding it hard to wrap your head around it by just reading the documentation, scroll all the way down to the examples and play around with those.

For some functions, vignettes are available. For instance, if you search for help on either of the two pivoting functions, you’ll be directed to a vignette on pivoting. Enter vignette("pivot") to access it. It’ll open in the Help window and is more like a detailed tutorial with lots of example data to help you understand more complex functions.

By the way: We haven’t had time to discuss all arguments that each of the functions we learned today can take, so if you think a particular function might almost solve your problem, check its documentation - maybe there’s an extra argument that does exactly what you’re looking for!

Working with Google results

If you encounter a specific problem but aren’t sure which function to use (or if you’re trying to use a command you know in a very specific way), it’s often a good idea to simply google the question (and the same goes for error messages - after you double-checked spelling, capitalisation, commas, missing brackets…). R generally has a large and helpful community, so you’ll likely find answers on sites such as Stack Overflow or, if you’re lucky, even entire blogposts written on a relevant issue.

Resources and cheat sheets

(16) Workflows/code conventions

Packages

When installing new packages, don’t put the code for that in your script - you don’t want packages to be installed every time you run a script when they’re already there. Always list and load all packages at the top of your script!

Naming conventions

Give expressive names to your variables and dataframes/tibbles. Don’t just call everything “data”, “df”, or “variable1”, “var1”, etc., but use names that immediately tell you what the variable contains. Use consistent naming conventions for variables. For example, if you use an underscore to separate words in one multi-word variable name, use the same symbol everywhere. Similarly, be consistent with capitalisation - the recommendation here is to use lowercase for all data and variable names to reduce the change of misspelling variables.

Line breaks

Throughout these materials, we’ve been using line breaks after pipes and often also after each argument of a function. This is purely for readability’s sake - optional, but highly recommended! We’ve also consistently assigned to the dataframe (e.g. spr <- spr %>% some commands) at the beginning of a code block, although it’s also possible to do that at the end of the code block.

Building up code blocks/chaining commands

When working on a (more) complex data wrangling problem, you’ll often need to take several steps towards your goal, i.e. use several commands to arrive at a tidy and workable dataframe. If you’re stuck at this step, think about what you want your data to look like, then identify single steps towards that form, then think about how to express each of these in R. It often helps to draw or take notes on paper to better think through this process! In terms of actually writing code, ideally also build it up step by step and don’t save your changes until you’re certain they are what you want. Sometimes, it can also be a good idea not to overwrite the original dataframe but to save it under a different name, e.g. for major restructuring such as pivoting.

Missing/questionable commands

LS0tDQp0aXRsZTogIldlbGNvbWUgdG8gdGhlIHRpZHl2ZXJzZSEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoqKlBhcnQgb2YgdGhlIERHZlMgUGhEIHN0dWRlbnRzJyBmb3J1bSwgMjMgRmVicnVhcnkgMjAyMSoqDQoNCkluc3RydWN0b3JzOiBLeWxhIE1jQ29ubmVsbCBhbmQgSnVsaWEgTcO8bGxlciAgDQpDb250YWN0IHVzIG9uIFR3aXR0ZXIgKEBNY2Nvbm5lbGxLeWxhLCBASnVsaWFNdWVsbGVyRnIpICANCg0KVW5sZXNzIGluZGljYXRlZCwgYXJ0d29yayBpcyBieSB0aGUgd29uZGVyZnVsIEBhbGxpc29uX2hvcnN0IC0gZmluZCBoZXIgb24gW2dpdGh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL2FsbGlzb25ob3JzdC9zdGF0cy1pbGx1c3RyYXRpb25zKS4NCg0KDQojICgxKSBXaGF0IGlzIHRoaXMgInRpZHl2ZXJzZSI/DQoNCkxldCdzIGp1bXAgcmlnaHQgaW4gYW5kIGxvYWQgdGhlIHBhY2thZ2U6DQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNClRoZSB0aWR5dmVyc2UgaXMgYW4gZXh0cmVtZWx5IHVzZWZ1bCBjb2xsZWN0aW9uIG9mIFIgKnBhY2thZ2VzKiAoaS5lLiBhZGQtb25zIHRvICpiYXNlLVIqKSB0aGF0IGhlbHAgeW91IGdldCB5b3VyIGRhdGEgaW50byBhIHVzZWZ1bCBmb3JtYXQgKGFtb25nIG90aGVyIHRoaW5ncykuDQoNCiFbVGhlIHRpZHl2ZXJzZV0oaW1nL3RpZHl2ZXJzZV9jZWxlc3RpYWwucG5nKXt3aWR0aD01MCV9DQoNCioqVGhlIGZvbGxvd2luZyBwYWNrYWdlcyBhcmUgaW5jbHVkZWQgaW4gdGhlIHRpZHl2ZXJzZToqKiAgDQotICpnZ3Bsb3QyKjogZm9yIGRhdGEgdmlzdWFsaXNhdGlvbiAgDQotICp0aWJibGUqOiBmb3IgdHdlYWtlZCBkYXRhZnJhbWVzICANCi0gKnRpZHlyKjogZm9yIGRhdGEgd3JhbmdsaW5nICANCi0gKnJlYWRyKjogZm9yIHJlYWRpbmcgaW4gZmlsZXMgIA0KLSAqcHVycnIqOiBmb3IgZnVuY3Rpb25hbCBwcm9nYW1taW5nICANCi0gKmRwbHlyKjogZm9yIGRhdGEgbWFuaXB1bGF0aW9uICANCi0gKnN0cmluZ3IqOiBmb3Igc3RyaW5nIG1hbmlwdWxhdGlvbiAgDQotICpmb3JjYXRzKjogZm9yIHdvcmtpbmcgd2l0aCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgKGZhY3RvcnMpIA0KDQoNCiMjIFdoYXQgaXMgdGlkeSBkYXRhLCBhbmQgd2h5IGRvIHdlIHVzZSBpdD8NCg0KKipDaGFyYWN0ZXJpc3RpY3Mgb2YgdGlkeSBkYXRhOioqICANCg0KIVtXaGF0IGRvZXMgdGlkeSBkYXRhIGxvb2sgbGlrZT9dKGltZy90aWR5ZGF0YV8xLmpwZyl7d2lkdGg9NTAlfQ0KDQoqKldoeSB0aGlzIGZvcm1hdD8qKiAgDQotIGEgbG90IG9mIHdyYW5nbGluZyBjb21tYW5kcyBhcmUgYmFzZWQgb24gdGhlIGFzc3VtcHRpb24gdGhhdCB5b3VyIGRhdGEgaXMgdGlkeSANCi0gaXMgZXhwZWN0ZWQgZm9yIG1hbnkgc3RhdGlzdGljYWwgbW9kZWxzDQotIHdvcmtzIGJlc3QgZm9yIHBsb3R0aW5nIA0KLSAiVGlkeSBkYXRhc2V0cyBhcmUgYWxsIGFsaWtlLCBidXQgZXZlcnkgbWVzc3kgZGF0YXNldCBpcyBtZXNzeSBpbiBpdHMgb3duIHdheSIgSGFkbGV5IFdpY2toYW0NCg0KIVtXaHkgdXNlIHRpZHkgZGF0YT9dKGltZy90aWR5ZGF0YV8zLmpwZyl7d2lkdGg9NTAlfQ0KDQoNCiMgKDIpIENvbW11bmljYXRpbmcgd2l0aCBSDQoNCiMjIFVuZGVyc3RhbmRpbmcgd2FybmluZ3MgYW5kIGVycm9ycw0KUiB3aWxsIG9mdGVuICJ0YWxrIiB0byB5b3Ugd2hlbiB5b3UncmUgcnVubmluZyBjb2RlLiBGb3IgZXhhbXBsZSwgd2hlbiB5b3UgaW5zdGFsbCBhIHBhY2thZ2UsIGl0J2xsIHRlbGwgeW91IGUuZy4gd2hlcmUgaXQgaXMgZG93bmxvYWRpbmcgYSBwYWNrYWdlIGZyb20sIGFuZCB3aGVuIGl0J3MgZG9uZS4gU2ltaWxhcmx5LCB3aGVuIHlvdSBsb2FkZWQgdGhlIHRpZHl2ZXJzZSBjb2xsZWN0aW9uIG9mIHBhY2thZ2VzLCBSIGxpc3RlZCB0aGVtIGFsbC4gVGhhdCdzIG5vdGhpbmcgdG8gd29ycnkgYWJvdXQhDQoNCldoZW4gdGhlcmUncyBhIG1pc3Rha2UgaW4gdGhlIGNvZGUgKGUuZy4gbWlzc3BlbGxpbmcgYSB2YXJpYWJsZSBuYW1lLCBmb3JnZXR0aW5nIHRvIGNsb3NlIHF1b3RhdGlvbiBtYXJrcyBvciBicmFja2V0cyksIFIgd2lsbCBnaXZlIGFuICplcnJvciogYW5kIGJlIHVuYWJsZSB0byBydW4gdGhlIGxpbmUuIFRoZSBlcnJvciBtZXNzYWdlIHdpbGwgZ2l2ZSB5b3UgaW1wb3J0YW50IGluZm9ybWF0aW9uIGFib3V0IHdoYXQgd2VudCB3cm9uZy4NCmBgYHtyfQ0KaGVsbG8gPC0gImhpIg0KI0hlbGxvDQpgYGANCg0KSW4gY29udHJhc3QsICp3YXJuaW5ncyogYXJlIHNob3duIHdoZW4gUiB0aGlua3MgdGhlcmUgY291bGQgYmUgYW4gaXNzdWUgd2l0aCB5b3VyIGNvZGUgb3IgaW5wdXQsIGJ1dCBpdCBzdGlsbCBydW5zIHRoZSBsaW5lLiBSIGlzIGdlbmVyYWxseSBqdXN0IHRlbGxpbmcgeW91IHRoYXQgeW91IE1JR0hUIGJlIG1ha2luZyBhIGxvZ2ljYWwgZXJyb3IsIG5vdCB0aGF0IHRoZSBjb2RlIGlzIGltcG9zc2libGUuDQoNCmBgYHtyfQ0KYygxLCAyLCAzKSArIGMoMSwgMikNCmBgYA0KDQoNCiMjIFJlYWRpbmcgZnVuY3Rpb24gZG9jdW1lbnRhdGlvbg0KV2UnbGwgZ2V0IHRvIGtub3cgYSBudW1iZXIgb2YgUiAqZnVuY3Rpb25zKiB0b2RheS4gVGhlc2UgZnVuY3Rpb25zIGNhbiB0YWtlIG9uZSBvciBtb3JlIGBhcmd1bWVudHNgLiBBcyBhbiBleGFtcGxlLCBsZXQncyB0cnkgb3V0IHRoZSBgc2F5KClgIGZ1bmN0aW9uIGZyb20gdGhlIGNvd3NheSBwYWNrYWdlLg0KDQpGaXJzdCwgKGluc3RhbGwgYW5kKSBsb2FkIHRoZSBjb3dzYXkgcGFja2FnZToNCmBgYHtyfQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJjb3dzYXkiKQ0KbGlicmFyeShjb3dzYXkpDQpgYGANCg0KVHJ5IHRoZSBmb2xsb3dpbmcgY29kZToNCmBgYHtyfQ0Kc2F5KA0KICB3aGF0ID0gIkdvb2QgbHVjayBsZWFybmluZyBSISIsIA0KICBieSA9ICJyYWJiaXQiKQ0KYGBgDQoNCldlIGNhbiBzZWUgdGhhdCB0aGlzIGZ1bmN0aW9uIGhhcyB0aGUgYHdoYXRgIGFyZ3VtZW50ICh3aGF0IHNob3VsZCBiZSBzYWlkPykgYW5kIHRoZSBgYnlgIGFyZ3VtZW50ICh3aGljaCBhbmltYWwgc2hvdWxkIHNheSBpdD8pLiBCdXQgd2hhdCBvdGhlciBvcHRpb25zIGFyZSB0aGVyZSBmb3IgdGhpcyBjb21tYW5kIC0gd2hpY2ggb3RoZXIgYW5pbWFscywgZm9yIGV4YW1wbGUsIG9yIGNhbiB5b3UgY2hhbmdlIHRoZSBjb2xvdXI/IFRvIHNlZSB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgdGhlIGBzYXlgIGNvbW1hbmQsIHlvdSBjYW4gZWl0aGVyIHJ1biB0aGlzIGxpbmUgb2YgY29kZToNCmBgYHtyfQ0KP3NheQ0KYGBgDQouLi5vciB0eXBlIGluIGBzYXlgIGluIHRoZSBIZWxwIHRhYiBvbiB0aGUgYm90dG9tIHJpZ2h0Lg0KDQpUaGlzIHdpbGwgc2hvdyB5b3UgdGhlIGRvY3VtZW50YXRpb24gZm9yIHRoZSBjb21tYW5kLg0KDQotICpVc2FnZSogc2hvd3MgYW4gb3ZlcnZpZXcgb2YgdGhlIGZ1bmN0aW9uIGFyZ3VtZW50cyBhbmQgdGhlaXIgZGVmYXVsdHMgKGUuZy4gaWYgeW91IHR5cGVkIGluIGBzYXkoKWAgd2l0aG91dCBhbnkgYXJndW1lbnRzIGluIHRoZSBicmFja2V0cywgeW91J2QgZ2V0IHRoZSBkZWZhdWx0cywgaS5lLiBhIGNhdCBzYXlpbmcgIkhlbGxvIHdvcmxkISIpDQpgYGB7cn0NCnNheSgpDQpgYGANCg0KLSAqQXJndW1lbnRzKiBwcm92aWRlcyBtb3JlIGluZm9ybWF0aW9uIG9uIGVhY2ggYXJndW1lbnQuIA0KQXJndW1lbnRzIGFyZSB0aGUgb3B0aW9ucyB5b3UgY2FuIHVzZSB3aXRoaW4gYSBmdW5jdGlvbi4NCi0gd2hhdA0KLSBieSANCi0gdHlwZQ0KLSB3aGF0X2NvbG9yDQpldGMuIA0KRWFjaCBvZiB0aGVzZSBjYW4gYmUgZmVkIHRoZSBgc2F5KClgIGZ1bmN0aW9uIHRvIHNsaWdodGx5IGFsdGVyIHdoYXQgaXQgZG9lcy4NCg0KLSAqRXhhbXBsZXMqIGF0IHRoZSBib3R0b20gb2YgdGhlIGhlbHAgcGFnZSBsaXN0cyBhIGZldyBleGFtcGxlcyB5b3UgY2FuIGNvcHktcGFzdGUgaW50byB5b3VyIGNvZGUgdG8gYmV0dGVyIHVuZGVyc3RhbmQgaG93IGEgZnVuY3Rpb24gd29ya3MuDQoNCkRvbid0IHdvcnJ5IGlmIHlvdSBkb24ndCB1bmRlcnN0YW5kIGV2ZXJ5dGhpbmcgaW4gdGhlIGRvY3VtZW50YXRpb24gd2hlbiB5b3UncmUgZmlyc3Qgc3RhcnRpbmcgb3V0LiBKdXN0IHRyeSB0byBnZXQgYW4gaWRlYSBmb3Igd2hpY2ggYXJndW1lbnRzIHRoZXJlIGFyZSBhbmQgd2hpY2ggb3B0aW9ucyBmb3IgdGhvc2UgYXJndW1lbnRzLiBJdCdzIGdvb2QgcHJhY3RpY2UgdG8gbG9vayBhdCBoZWxwIGRvY3VtZW50cyBvZnRlbiAtLSB0aGlzIHdpbGwgYWxzbyBoZWxwIHlvdSBnZXQgbW9yZSBlZmZpY2llbnQgYXQgZXh0cmFjdGluZyB0aGUgaW5mbyB5b3UgbmVlZCBmcm9tIHRoZW0uDQoNCg0KIyAoMykgUmVhZGluZyBpbiBhbmQgZXhwbG9yaW5nIGRhdGENCg0KV2hhdCB0eXBlIG9mIGZpbGUgYXJlIHlvdSB3b3JraW5nIHdpdGg/IFNwZWNpZmljYWxseSwgd2hhdCdzIHVzZWQgdG8gc2VwYXJhdGUgdGhlIHZhbHVlcyBpbiB0aGUgZGlmZmVyZW50IGNvbHVtbnM/IFRvIGZpbmQgb3V0LCBvcGVuIHRoZSBmaWxlIGluIGEgdGV4dCBlZGl0b3IuIENvbW1vbiBvcHRpb25zIGFyZSBlaXRoZXIgY29tbWFzIG9yIHNlbWljb2xvbnMgKGluIHdoaWNoIGNhc2VzLCB0aGUgZmlsZSBvZnRlbiBoYXMgdGhlIGVuZGluZyAuY3N2KSBvciB0YWJzIChvZnRlbiAudHh0IGZpbGVzKS4NCg0KV2hlcmUgaXMgdGhlIGZpbGUgc2F2ZWQ/IElmIHlvdSBhcmUgd29ya2luZyBpbiBhIE1hcmtkb3duIGFuZCB0aGUgZGF0YSBmaWxlIGlzIHNhdmVkIGluIHRoZSBzYW1lIGxvY2F0aW9uIGFzIHRoZSBzY3JpcHQgZmlsZSwgeW91IGNhbiB1c2UgdGhlIG5hbWUgb2YgdGhlIGZpbGUgd2l0aCBpdHMgZW5kaW5nLCBlLmcuICJuZXdfZGF0YS5jc3YiIG9yICJmaW5hbF9kYXRhLnR4dCIuIFRoaXMgaXMgcG9zc2libGUgYmVjYXVzZSBSTWFya2Rvd24gYXV0b21hdGljYWxseSBzZXRzIHRoZSB3b3JraW5nIGRpcmVjdG9yeSwgaS5lLiB0aGUgbG9jYXRpb24gUiB0cmllcyB0byBmaW5kIHRoZSBmaWxlLCB0byB3aGVyZSBpdCBpcyBzYXZlZC4gVGhpcyBpcyB2ZXJ5IGNvbnZlbmllbnQgZXNwZWNpYWxseSB3aGVuIHlvdSBzaGFyZSB5b3VyIHNjcmlwdCBhbmQgZGF0YS4gWW91ciBjb250cmlidXRvciBkb2Vzbid0IG5lZWQgdG8gdHlwZSBpbiBhbnkgbG9uZyBwYXRocyBidXQgY2FuIGRpcmVjdGx5IHN0YXJ0IHdvcmtpbmcuDQpJZiB0aGUgZmlsZSBpcyBzYXZlZCBpbiBhIHN1YmZvbGRlciBjYWxsZWQgImRhdGEiLCB1c2UgICJkYXRhL2V4YW1wbGVfZmlsZS5jc3YiKS4gLi4vIGFsbG93cyB5b3UgdG8gZ28gYmFja3dhcmRzIG9uZSBmb2xkZXIuDQoNCklmIHlvdSdyZSB3b3JraW5nIGluIGEgc2NyaXB0LCB1c2UgYHNldHdkKClgIChlLmcuOiBgc2V0d2QofkRvY3VtZW50cy9QaEQvUnNjcmlwdHMvLi4uKWAgb3IgYHNldHdkKEM6L0RvY3VtZW50cy4uLilgKSwgb3IgU2Vzc2lvbiAtPiBTZXQgV29ya2luZyBEaXJlY3RvcnkgLT4gQ2hvb3NlIERpcmVjdG9yeS4uLikNCg0KVGhlIGNvbW1hbmRzIHRvIHJlYWQgaW4gYSBmaWxlIGFyZToNCi0gYmFzZS1SOiBgcmVhZC5jc3YoKWAgYW5kIGByZWFkLnRzdigpYA0KICAtIHRpZHl2ZXJzZSAoaW1wcm92ZW1lbnRzLCByZWFkcyBhcyB0aWJibGUgaW5zdGVhZCBvZiBkYXRhZnJhbWUpOiBgcmVhZF9jc3YoKWAgYW5kIGByZWFkX3RzdigpYA0KICAgIC0gbXVzdCBoYXZlIHRpZHl2ZXJzZSBpbnN0YWxsZWQgYW5kIGxvYWRlZCB3aXRoIGEgYGxpYnJhcnkoKWAgY2FsbA0KICAgIC0gYHJlYWRfY3N2MigpYCBmb3Igc2VtaWNvbG9uLXNlcGFyYXRlZCBjc3YgDQogICAgLSBgcmVhZF9kZWxpbShmaWxlLCBkZWxpbSlgIGZvciBhbnkgb3RoZXIgZGVsaW1pdGVycw0KICAgIA0KKlNhdmUgdGhlIG91dHB1dCB0byBhIHZhcmlhYmxlIHVzaW5nIDwtICoNCg0KU28gcmVhZGluZyBpbiBkYXRhIHRlbmRzIHRvIGZvbGxvdyB0aGlzIHBhdHRlcm46DQpgYGB7ciBldmFsPUZBTFNFfQ0KbmFtZV9vZl9kYXRhX2luX1IgPC0gcmVhZF9jc3YoImRhdGFfZmlsZS5jc3YiKSAjIGVxdWl2YWxlbnQgdG8NCm5hbWVfb2ZfZGF0YV9pbl9SIDwtIHJlYWRfZGVsaW0oImRhdGFfZmlsZS5jc3YiLCBkZWxpbSA9ICIsIikNCg0KbmFtZV9vZl9kYXRhX2luX1IgPC0gcmVhZF9jc3YyKCJkYXRhX2ZpbGUuY3N2IikgIyBlcXVpdmFsZW50IHRvDQpuYW1lX29mX2RhdGFfaW5fUiA8LSByZWFkX2RlbGltKCJkYXRhX2ZpbGUuY3N2IiwgZGVsaW0gPSAiOyIpDQoNCm5hbWVfb2ZfZGF0YV9pbl9SIDwtIHJlYWRfdHN2KCJkYXRhX2ZpbGUudHh0IikgIyB0YWItc2VwYXJhdGVkIGZpbGUNCmBgYA0KDQoNCiMjIyMgQSBub3RlIG9uIHRoZSBkYXRhIHdlJ2xsIHVzZSB0b2RheQ0KV2UndmUgdHJpZWQgdG8gdGFpbG9yIHRoZSB3b3Jrc2hvcCB0byBiZSByZWxldmFudCB0byB0aGUga2luZHMgb2YgZGF0YSBtYW55IG9mIHlvdSBzYWlkIHlvdSB1c2VkIGluIHRoZSBwcmUtd29ya3Nob3Agc3VydmV5LCBzbyB3ZSdsbCBzdGFydCB3aXRoIHNlbGYtcGFjZWQgcmVhZGluZyBkYXRhICh3aGljaCBsb29rcyBmYWlybHkgc2ltaWxhciB0byBleWUtdHJhY2tpbmcgZGF0YSwgYW5vdGhlciBjb21tb24gcmVzcG9uc2UpLCBhbmQgbGF0ZXIgdXNlIGV4YW1wbGVzIG9mIGNvcnB1cyBkYXRhIGFuZCBhIHF1ZXN0aW9ubmFpcmUgb3V0cHV0LiBIb3dldmVyLCBldmVyeXRoaW5nIHdlIGRpc2N1c3MgaXMgdXNlZnVsIGZvciBhbnkga2luZCBvZiBkYXRhIQ0KV2UncmUgYWxzbyBwcm92aWRpbmcgc2V2ZXJhbCBmaWxlcyBmb3IgeW91IHRvIHByYWN0aXNlIG9uIGxhdGVyLCBzbyB5b3UgY2FuIHBpY2sgZGF0YSB0aGF0IGxvb2tzIGNsb3Nlc3QgdG8gd2hhdCB5b3UncmUgYWN0dWFsbHkgd29ya2luZyB3aXRoLg0KDQpMZXQncyByZWFkIGluIGEgc21hbGwgc2VsZi1wYWNlZCByZWFkaW5nIGRhdGFzZXQgKHNhdmVkIGluIHRoZSBkYXRhIGZvbGRlciwgc28gd2UgbmVlZCB0byBhZGQgYGRhdGEvYCB0byB0ZWxsIFIgdGhhdCk6DQpgYGB7cn0NCnNwciA8LSByZWFkX2NzdigiZGF0YS9kZ2ZzX3Nwci5jc3YiKQ0KYGBgDQpUaGUgY3VycmVudCBkYXRhZnJhbWUgaXMgYSBzZWxmLXBhY2VkIHJlYWRpbmcgZXhwZXJpbWVudCB3aGVyZSAxMiBwYXJ0aWNpcGFudHMgcmVhZCAyMCBzZW50ZW5jZXMgZWFjaCwgcGx1cyAzIHByYWN0aWNlIHNlbnRlbmNlcyB0byBnZXQgdGhlbSB3YXJtZWQgdXAuIEhhbGYgdGhlIHNlbnRlbmNlcyB3ZXJlIGFib3V0IGRvZ3MgYW5kIGhhbGYgdGhlIHNlbnRlbmNlcyB3ZXJlIGFib3V0IGNhdHMuIEluIG9uZSBjb25kaXRpb24gKEEpLCBhbGwgc2VudGVuY2VzIHdlcmUgcGFpcmVkIHdpdGggYXBwcm9wcmlhdGUgYWRqZWN0aXZhbCBjb2xsb2NhdGVzIGFjY29yZGluZyB0byB0aGUgQk5DIChsYXAgZG9nIHZzLiB0b3J0b2lzZXNoZWxsIGNhdCksIGluIHRoZSBvdGhlciwgdGhlc2Ugd2VyZSByZXZlcnNlIChsYXAgY2F0IHZzLiB0b3J0b2lzZXNoZWxsIGRvZykuIEFsbCBzZW50ZW5jZXMgd2VyZSBwcmVzZW50ZWQgaW4gb3RoZXJ3aXNlIG5hdHVyYWwtc291bmRpbmcgc2VudGVuY2VzLiANCg0KIyMgRXhwbG9yaW5nIG91ciBkYXRhDQoNCk5vdyB5b3UgaGF2ZSBhIGRhdGEgZmlsZSByZWFkIGluLCBidXQgaG93IGRvIHlvdSBzZWUgd2hhdCdzIGluIGl0Pw0KDQpgYGB7cn0NCmhlYWQoc3ByKQ0KYGBgDQoNCllvdSBjYW4gY2hhbmdlIHRoZSBhbW91bnQgb2Ygcm93cyB3aXRoIHRoZSBgbmAgKmFyZ3VtZW50KjogDQpgYGB7cn0NCmhlYWQoc3ByLCBuPTMpDQpgYGANCg0KT3I6IGNsaWNrIG5hbWUgb2YgZGF0YWZyYW1lIGluIEVudmlyb25tZW50IHRhYg0KICAtIGNhbiBhbHNvIHNvcnQgY29sdW1ucyBhbmQgRmlsdGVyIHJvd3MgLS0ganVzdCBmb3Igdmlld2luZyBwdXJwb3Nlcw0KICAtIGJpdCBzbG93IGlmIHlvdSBzdGFydCBoYXZpbmcgaHVnZSBkYXRhZnJhbWVzIGJ1dCBvZnRlbiBhIGdvb2QgZmlyc3QgbG9vaw0KDQpUaGVyZSdzIGFsc28gYW4gZWFzeSB3YXkgdG8gc2VlIHdoYXQgdGhlIGNvbHVtbnMgYXJlOg0KYGBge3J9DQpjb2xuYW1lcyhzcHIpDQpgYGANCg0KYHN1bW1hcnkoKWA6IGNhbGwgaXQgb24gYSBkYXRhZnJhbWUgdG8gZ2V0IGVhY2ggY29sdW1uIGFuZCB1c2VmdWwgaW5mbyBiYXNlZCBvbiB0aGUgZGF0YSB0eXBlLiBGb3IgZXhhbXBsZSwgbnVtZXJpYyBjb2x1bW5zIHdpbGwgc2hvdyB0aGUgbWluLCBtZWRpYW4sIG1heCBhbmQgdGhlIHF1YXJ0aWxlcyAoMjUlIGluY3JlbWVudHMpLg0KYGBge3J9DQpzdW1tYXJ5KHNwcikNCg0Kc3VtbWFyeShzcHIkUlQpDQpgYGANCg0KIyMjIEV4ZXJjaXNlDQpSZWFkIGluIGFuZCBleHBsb3JlIHRoZSBleGFtcGxlIGRhdGENCkhvdyBtYW55IHJvd3MgYW5kIGNvbHVtbnMgZG9lcyBpdCBoYXZlPw0KDQojIyBDaGFyYWN0ZXIgdnMuIGZhY3RvciBjb2x1bW5zDQoNCkluIHRoZSBlbnZpcm9ubWVudCBwYW5lbCAob3IgdXNpbmcgYHN0cigpYCksIHlvdSBjYW4gc2VlIHRoYXQgYWxsIHRoZSB2YXJpYWJsZXMgaW4gdGhpcyBkYXRhIGFyZSByZWFkIGluIGFzIGVpdGhlciBudW1lcmljIG9yIGNoYXJhY3RlciBkYXRhLiBIb3dldmVyLCBzb21lIHZhcmlhYmxlcyBzaG91bGQgYmUgdHJlYXRlZCBhcyBmYWN0b3JzIGJlY2F1c2UgdGhleSByZXByZXNlbnQgY2F0ZWdvcmllcywgbm90IHRleHQgZGF0YS4gTGV0J3MgY29udmVydCB0aGVtLiANCmBgYHtyfQ0Kc3ByJHBhcnRpY2lwYW50IDwtIGFzX2ZhY3RvcihzcHIkcGFydGljaXBhbnQpDQpzcHIkaXRlbV90eXBlIDwtIGFzX2ZhY3RvcihzcHIkaXRlbV90eXBlKQ0Kc3ByJGNvbmQgPC0gYXNfZmFjdG9yKHNwciRjb25kKQ0KYGBgDQoNClRoaXMgd2F5LCB0aGUgc3VtbWFyeSBvdXRwdXQgYWxzbyB3b3JrcyBhcyBleHBlY3RlZC4gRm9yIGV4YW1wbGUsIHdlIGNhbiBoYXZlIGEgbG9vayBhdCBob3cgbWFueSBkaWZmZXJlbnQgcGFydGljaXBhbnRzIGFuZCBjb25kaXRpb25zOg0KYGBge3J9DQpzdW1tYXJ5KHNwciRwYXJ0aWNpcGFudCkNCg0Kc3VtbWFyeShzcHIkY29uZCkNCmBgYA0KDQpJZiBhbnkgb3RoZXIgY29sdW1ucyBpbiB5b3VyIGRhdGFmcmFtZSBhcmUgcmVhZCBpbiB3cm9uZyAoZm9yIGV4YW1wbGUsIGlmIHlvdSBoYXZlIGEgbnVtZXJpYyBjb2x1bW4gdGhhdCBsb29rcyBsaWtlOiAiNDMiLCAiMTgiIGFuZCBpcyBiZWluZyByZWFkIGFzIGEgY2hhcmFjdGVyIGNvbHVtbikgeW91IGNhbiBjb252ZXJ0IHRoZW0gd2l0aCBzaW1pbGFyIHN5bnRheDogDQpgYXMubnVtZXJpYygpYCwgYGFzLmNoYXJhY3RlcigpYCBldGMuDQoNCg0KIyAoNCkgVGhlIHBpcGUgJT4lIA0KDQpPbmUgb2YgdGhlIG1vc3Qgbm90aWNlYWJsZSBmZWF0dXJlcyBvZiB0aGUgdGlkeXZlcnNlIGlzIHRoZSBwaXBlICU+JSAoa2V5Ym9hcmQgc2hvcnRjdXQ6IEN0ci9DbWQgKyBTaGlmdCArIE0pIC4NCg0KVGhlIHBpcGUgdGFrZXMgdGhlIGl0ZW0gYmVmb3JlIGl0IGFuZCBmZWVkcyBpdCB0byB0aGUgZm9sbG93aW5nIGNvbW1hbmQgYXMgdGhlIGZpcnN0IGFyZ3VtZW50LiBTaW5jZSBhbGwgdGlkeXZlcnNlIChhbmQgc29tZSBub24tdGlkeXZlcnNlKSBmdW5jdGlvbnMgdGFrZSB0aGUgZGF0YWZyYW1lIGFzIHRoZSBmaXJzdCBhcmd1bWVudCwgdGhpcyBjYW4gYmUgdXNlZCB0byBzdHJpbmcgdG9nZXRoZXIgbXVsdGlwbGUgZnVuY3Rpb25zLiANCg0KU28gdG8gcmUtd3JpdGUgdGhlIGBoZWFkKClgIGZ1bmN0aW9uIHdpdGggdGhlIHBpcGU6DQpgYGB7cn0NCnNwciAlPiUgDQogIGhlYWQoKQ0KYGBgDQoNClRoaXMgcHJvZHVjZXMgdGhlIGV4YWN0IHNhbWUgb3V0cHV0IGFzIGBoZWFkKHNwcilgLiBXaHkgd291bGQgdGhpcyBiZSB1c2VmdWw/DQoNCllvdSBjYW4gc2VlIHRoYXQgdGhlIHZlcnNpb24gd2l0aCB0aGUgcGlwZSBpcyBlYXNpZXIgdG8gcmVhZCB3aGVuIG1vcmUgdGhhbiBvbmUgZnVuY3Rpb24gaXMgY2FsbGVkIG9uIHRoZSBzYW1lIGRhdGFmcmFtZSEgDQoNCkhlcmUgYXJlIHNvbWUgbW9yZSBleGFtcGxlczoNCmBgYHtyfQ0KIyBFcXVpdmFsZW50IHRvIHN1bW1hcnkoc3ByKQ0Kc3ByICU+JSANCiAgc3VtbWFyeSgpDQoNCiMgRXF1aXZhbGVudCB0byBjb2xuYW1lcyhzcHIpLCB3aGljaCByZXR1cm5zIGFsbCBjb2x1bW4gbmFtZXMNCnNwciAlPiUgDQogIGNvbG5hbWVzKCkgDQoNCiMgRXF1aXZhbGVudCB0byBucm93KHNwciksIHdoaWNoIHJldHVybnMgdGhlIG51bWJlciBvZiByb3dzIGluIHRoZSBkZg0Kc3ByICU+JSANCiAgbnJvdygpDQpgYGANCg0KQW5kIHlvdSBjYW4gYWxzbyBzdGFjayBjb21tYW5kcyBieSBlbmRpbmcgZWFjaCByb3cgKGV4Y2VwdCB0aGUgbGFzdCBvbmUpIHdpdGggYSBwaXBlOg0KYGBge3J9DQpzcHIgJT4lIA0KICBjb2xuYW1lcygpICU+JSAjZXh0cmFjdHMgY29sdW1uIG5hbWVzDQogIG5jaGFyKCkgI2NvdW50cyB0aGUgbnVtYmVyIG9mIGxldHRlcnMNCmBgYA0KDQoNCiMgKDUpIFJlbmFtaW5nIGFuZCByZWFycmFuZ2luZyBkYXRhDQoNCiMjIHJlbmFtZSgpDQpZb3UgY2FuIHJlbmFtZSBjb2x1bW5zIHdpdGggdGhlIGByZW5hbWUoKWAgZnVuY3Rpb24uIFRoZSBzeW50YXggaXMgbmV3X25hbWUgPSBvbGRfbmFtZS4gTGV0J3MgcmVuYW1lIHRoZSBgY29uZGAgdmFyaWFibGU6DQpgYGB7cn0NCnNwciAlPiUgDQogIHJlbmFtZShjb25kaXRpb24gPSBjb25kKQ0KYGBgDQoNClRoaXMgaXMganVzdCBhIHByZXZpZXcgYmVjYXVzZSB3ZSBkaWRuJ3QgYXNzaWduIHRoZSBjaGFuZ2VkIGRhdGFmcmFtZSB0byBhbnkgbmFtZS4gVGhpcyBpcyB1c2VmdWwgZm9yIHRlc3RpbmcgY29kZSBhbmQgbWFraW5nIHN1cmUgaXQgZG9lcyB3aGF0IHlvdSBleHBlY3QgYW5kIHdhbnQgaXQgdG8gZG8uDQoNCi0gaWYgeW91IGxvb2sgYXQgdGhlIHNwciBkYXRhZnJhbWUsIGZvciBleGFtcGxlIGluIHRoZSBFbnZpcm9ubWVudCBwYW5lbCBvbiB0aGUgdXBwZXItcmlnaHQsIHRoZSBkYXRhZnJhbWUgaGFzbid0IGNoYW5nZWQNCg0KLSBUbyBzYXZlIHlvdXIgY2hhbmdlczogYXNzaWduaW5nIHlvdXIgY2FsbCBiYWNrIHRvIHRoZSB2YXJpYWJsZSBuYW1lDQoNCi0gR29vZCB3b3JrIGZsb3c6IFByZXZpZXcgdGhlbiBzYXZlIHdoZW4geW91J3JlIHN1cmUgeW91J3JlIGhhcHB5IHdpdGggdGhlIG91dHB1dA0KDQpZb3UgY2FuIGFsc28gcmVuYW1lIG11bHRpcGxlIGNvbHVtbnMgYXQgb25jZSAobm8gbmVlZCBmb3IgYW4gYXJyYXkgaGVyZSk6DQpgYGB7cn0NCnNwciA8LSBzcHIgJT4lIA0KICByZW5hbWUoY29uZGl0aW9uID0gY29uZCwgDQogICAgICAgICBzZW50ZW5jZSA9IGZ1bGxfc2VudGVuY2UpDQpgYGANCk5vdGljZSBhYm92ZSB0aGF0IEkndmUgc2F2ZWQgb3V0cHV0IG92ZXIgdGhlIHNwciBkYXRhZnJhbWUgdG8gbWFrZSB0aGUgY2hhbmdlcyAncGVybWFuZW50Jy4NCg0KVGhlcmUgaXMgbm8gb3V0cHV0IHdoZW4geW91IHNpbXBseSBzYXZlIHRoZSByZXNwb25zZSwgYnV0IHRoZSBzcHIgZGF0YWZyYW1lIGhhcyBiZWVuIHBlcm1hbmVudGx5IHVwZGF0ZWQgKHdpdGhpbiB0aGUgUiBzZXNzaW9uLCBub3QgaW4geW91ciBmaWxlIHN5c3RlbSkNCg0KSWYgeW91IG1ha2UgYSBtaXN0YWtlOiBhcnJvdyB3aXRoIGEgbGluZSB1bmRlciBpdCBpbiB0aGUgY29kZSBibG9jayBvZiBSLU1hcmtkb3duLCBydW5zIGFsbCBibG9ja3MgYWJvdmUgKGJ1dCBub3QgdGhlIGN1cnJlbnQgb25lKQ0KDQoNCiMjIGFycmFuZ2UoKQ0KDQpUaGVyZSdzIGFsc28gYSBjb3JyZXNwb25kaW5nIGNvbW1hbmQgdGhhdCBsZXRzIHlvdSBzb3J0IGJ5IHJvdyB2YWx1ZXM6IGBhcnJhbmdlKClgLiBCeSBkZWZhdWx0LCB0aGlzIHNvcnRzIGJ5IGxvd2VzdCB0byBoaWdoZXN0IHZhbHVlLCBidXQgeW91IGNhbiBhZGQgYGRlc2MoKWAgdG8gcmV2ZXJzZSB0aGF0Lg0KYGBge3J9DQpzcHIgJT4lIA0KICBhcnJhbmdlKFJUKQ0KDQpzcHIgJT4lIA0KICBhcnJhbmdlKGRlc2MoUlQpKQ0KYGBgDQoNCg0KIyAoNikgU3Vic2V0cw0KDQojIyBzZWxlY3QoKQ0KDQpUaGUgdHJhZGl0aW9uYWwgc3ludGF4IGZvciBkZWFsaW5nIHdpdGggY29sdW1ucyBpcyBkYXRhZnJhbWUkY29sdW1uLg0KQSB1c2VmdWwgc3RlcCBpbiB1c2luZyBwaXBlcyBhbmQgdGlkeXZlcnNlIGNhbGxzIGlzIHRoZSBhYmlsaXR5IHRvICpzZWxlY3QqIHNwZWNpZmljIGNvbHVtbnMuIFRoYXQgaXMsIGluc3RlYWQgb2Ygd3JpdGluZyBgc3ByJFJUYCB3ZSBjYW4gd3JpdGU6DQpgYGB7cn0NCnNwciAlPiUgDQogIHNlbGVjdChSVCkgDQpgYGANCg0KDQojIyMgU2VsZWN0IG11bHRpcGxlIGNvbHVtbnMgDQpZb3UgY2FuIGFsc28gdXNlIGBzZWxlY3QoKWAgdG8gdGFrZSBtdWx0aXBsZSBjb2x1bW5zLiANCg0KYGBge3J9DQpzcHIgJT4lIA0KICBzZWxlY3QocGFydGljaXBhbnQsIHdvcmQsIFJUKQ0KYGBgDQoNCllvdSBjYW4gc2VlIHRoYXQgdGhlc2UgY29sdW1ucyBhcmUgcHJlc2VudGVkIGluIHRoZSBvcmRlciB5b3UgZ2F2ZSB0aGVtIHRvIHRoZSBzZWxlY3QgY2FsbCwgdG9vOg0KDQpgYGB7cn0NCnNwciAlPiUgDQogIHNlbGVjdChSVCwgd29yZCwgcGFydGljaXBhbnQpDQpgYGANCg0KWW91IGNhbiBhbHNvIHVzZSB0aGlzIHRvIHJlb3JkZXIgY29sdW1ucyBpbiB0aGF0IHlvdSBnaXZlIHRoZSBuYW1lIG9mIHRoZSBjb2x1bW4ocykgeW91IHdhbnQgZmlyc3QsIHRoZW4gZmluaXNoIHdpdGggYGV2ZXJ5dGhpbmcoKWAgdG8gaGF2ZSBhbGwgb3RoZXIgY29sdW1ucyBmb2xsb3c6DQoNCmBgYHtyfQ0Kc3ByICU+JSANCiAgc2VsZWN0KFJULCBldmVyeXRoaW5nKCkpDQpgYGANCg0KDQojIyMgUHJldmlldyB2cy4gc2F2aW5nDQoNCkFib3ZlLCB3ZSdyZSBtb3N0bHkgcHJldmlld2luZyB1c2luZyBgc2VsZWN0KClgIGZvciBmaXJzdCBpbnNpZ2h0cy4gSWYgeW91IGxvb2sgYXQgdGhlIHNwciBkYXRhZnJhbWUsIGZvciBleGFtcGxlIGluIHRoZSBFbnZpcm9ubWVudCBwYW5lbCBvbiB0aGUgdXBwZXItcmlnaHQsIHRoZSBkYXRhZnJhbWUgaGFzbid0IGNoYW5nZWQuIFRvIHNhdmUgeW91ciBjaGFuZ2VzLCBhc3NpZ24geW91ciBjYWxsIGJhY2sgdG8gdGhlIHZhcmlhYmxlIG5hbWUsIGkuZS4NCmRmIDwtIGRmICU+JSANCiAgc29tZSBvcGVyYXRpb25zIGhlcmUNCg0KDQojIyMgUmVtb3ZlIGNvbHVtbnMgd2l0aCBzZWxlY3QNCg0KWW91IGNhbiBhbHNvIHJlbW92ZSBjb2x1bW5zIHVzaW5nIHNlbGVjdCBpZiB5b3UgdXNlIHRoZSBtaW51cyBzaWduLiBGb3IgZXhhbXBsZSwgdGhlIGl0ZW1fdHlwZSBjb2x1bW4gaXMgYSBmYWN0b3Igd2l0aCBvbmx5IG9uZSBsZXZlbCAtIGl0IGFsd2F5cyBzYXlzICJEYXNoZWRTZW50ZW5jZSIuIFNvIGxldCdzIGdldCByaWQgb2YgaXQ6DQoNCmBgYHtyfQ0Kc3ByICU+JSANCiAgc2VsZWN0KC1pdGVtX3R5cGUpDQpgYGANCg0KWW91IGNhbiBhbHNvIHJlbW92ZSBtdWx0aXBsZSBjb2x1bW5zIGF0IG9uY2UgYnkgd3JpdGluZyB0aGVtIGluIGFuIGFycmF5IGBjKClgLiBXZSdkIGxpa2UgdG8gcmVtb3ZlIHRoZSBpdGVtIHR5cGUgY29sdW1uIGFuZCBhbHNvIHRoZSBmaXJzdCBjb2x1bW4gKFgxKSB3aGljaCBzZWVtcyB0byBiZSBqdXN0IGEgY291bnRlci4NCmBgYHtyfQ0Kc3ByIDwtIHNwciAlPiUgDQogIHNlbGVjdCgtYyhpdGVtX3R5cGUsIFgxKSkNCmBgYA0KVGhpcyBvdmVyd3JpdGVzIHRoZSBkYXRhIGFzIGl0IGlzIHNhdmVkIGluIFIuIEl0IGRvZXMgKipub3QqKiBvdmVyd3JpdGUgdGhlIGZpbGUgdGhhdCBpcyBzYXZlZCBvbiB5b3VyIGNvbXB1dGVyLg0KDQoNCiMjIyBMZXZlbGluZyB1cCBzZWxlY3QoKQ0KDQpVbnRpbCBub3csIHdlJ3ZlIHVzZWQgc2VsZWN0KCkgaW4gY29tYmluYXRpb24gd2l0aCB0aGUgZnVsbCBjb2x1bW4gbmFtZSwgYnV0IHRoZXJlIGFyZSBoZWxwZXIgZnVuY3Rpb25zIHRoYXQgbGV0IHlvdSBzZWxlY3QgY29sdW1ucyBiYXNlZCBvbiBvdGhlciBjcml0ZXJpYS4NCg0KRm9yIGV4YW1wbGUsIGhlcmUncyBob3cgd2UgY2FuIHNlbGVjdCBib3RoIHRoZSBgc2VudGVuY2VfbnVtYCBhbmQgdGhlIGB3b3JkX251bWAgY29sdW1uIC0gYnkgc3BlY2lmeWluZyBgZW5kc193aXRoKCJfbnVtIilgIGluIHRoZSBzZWxlY3QoKSBjYWxsOg0KYGBge3J9DQpzcHIgJT4lIA0KICBzZWxlY3QoZW5kc193aXRoKCJfbnVtIikpDQpgYGANCg0KVGhlIG9wcG9zaXRlIGlzIGFsc28gcG9zc2libGUgdXNpbmcgYHN0YXJ0c193aXRoKClgIA0KDQpgY29udGFpbnNgIGlzIGFub3RoZXIgaGVscGVyIGZ1bmN0aW9uLiBIZXJlLCB3ZSdyZSB1c2luZyBpdCB0byBzaG93IGFsbCBjb2x1bW5zIHRoYXQgY29udGFpbiBhbiB1bmRlcnNjb3JlOg0KYGBge3J9DQpzcHIgJT4lIA0KICBzZWxlY3QoY29udGFpbnMoIl8iKSkNCmBgYA0KDQpXZSBjYW4gYWxzbyBzZWxlY3QgYSByYW5nZSBvZiAgdmFyaWFibGVzIHVzaW5nIGEgY29sb24uIFRoaXMgd29ya3MgYm90aCB3aXRoIHZhcmlhYmxlcyBhbmQgKGEgcmFuZ2Ugb2YpIG51bWJlcnM6DQpgYGB7cn0NCnNwciAlPiUgDQogIHNlbGVjdChjb25kaXRpb246d29yZCkgIyBzaG93cyBjb25kaXRpb24sIHdvcmRfbnVtLCB3b3JkDQoNCnNwciAlPiUgDQogIHNlbGVjdCgxOjMpICMgZmlyc3QgdGhyZWUgY29sdW1ucw0KYGBgDQpIZXJlLCB0aGUgb3JkZXIgb2YgdGhlIGNvbHVtbnMgbWF0dGVycyENCg0KT3RoZXIgaGVscGVyIGZ1bmN0aW9ucyBhcmU6DQotIG1hdGNoZXM6IHNpbWlsYXIgdG8gY29udGFpbnMsIGJ1dCBjYW4gdXNlIHJlZ3VsYXIgZXhwcmVzc2lvbnMNCi0gbnVtX3JhbmdlOiBpbiBhIGRhdGFzZXQgd2l0aCB0aGUgdmFyaWFibGVzIFgxLCBYMiwgWDMsIFg0LCBZMSwgYW5kIFkyLCBzZWxlY3QobnVtX3JhbmdlKCJYIiwgMTozKSkgcmV0dXJucyBYMSwgWDIsIGFuZCBYMw0KDQoNCiMjIGZpbHRlcigpDQoNCiMjIyBGaWx0ZXIgYmFzZWQgb24gYSBjb25kaXRpb24NCg0KV2hpbGUgd2l0aCBgc2VsZWN0KClgLCB5b3UgY2FuIHBpY2sgKipjb2x1bW5zKiogYnkgbmFtZSBvciBpZiB0aGV5IGZ1bGZpbGwgY29uZGl0aW9ucywgYGZpbHRlcigpYCBsZXRzIHlvdSBsb29rIGZvciAqKnJvd3MqKiB0aGF0IGZ1bGZpbGwgY2VydGFpbiBjb25kaXRpb25zLiANCg0KIVtGaWx0ZXJdKGltZy9kcGx5cl9maWx0ZXIuanBnKXt3aWR0aD01MCV9DQoNClVzZSBgZmlsdGVyKClgIHRvIHJldHVybiBhbGwgaXRlbXMgdGhhdCBmaXQgYSBjZXJ0YWluIGNvbmRpdGlvbi4gRm9yIGV4YW1wbGUsIHlvdSBjYW4gdXNlOg0KLSBlcXVhbHMgdG86ID09DQotIG5vdCBlcXVhbCB0bzogIT0NCi0gZ3JlYXRlciB0aGFuOiA+IA0KLSBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG86ID49DQotIGxlc3MgdGhhbjogPA0KLSBsZXNzIHRoYW4gb3IgZXF1YWwgdG86IDw9DQotIGluIChpLmUuIGluIGEgdmVjdG9yKTogJWluJQ0KDQoqKlN5bnRheDoqKg0KZmlsdGVyKGRhdGEsIGNvbHVtbm5hbWUgbG9naWNhbC1vcGVyYXRvciBjb25kaXRpb24pDQpvciwgdXNpbmcgdGhlIHBpcGU6DQpkYXRhICU+JSANCiAgZmlsdGVyKGNvbHVtbm5hbWUgbG9naWNhbC1vcGVyYXRvciBjb25kaXRpb24pDQoNCkxldCdzIGxvb2sgYXQgcmVhY3Rpb24gdGltZXMgdGhhdCBhcmUgc2hvcnRlciB0aGFuIDIwMCBtczoNCmBgYHtyfQ0Kc3ByICU+JSANCiAgZmlsdGVyKFJUIDwgMjAwKQ0KYGBgDQoNCi4uLnJlYWN0aW9uIHRpbWVzIGxvbmdlciB0aGFuIG9yIGVxdWFsIHRvIDI1MCBtczoNCmBgYHtyfQ0Kc3ByICU+JSANCiAgZmlsdGVyKFJUID49IDI1MCkNCmBgYA0KDQpPciB5b3UgY2FuIHVzZSBpdCB0byBzZWxlY3QgYWxsIGl0ZW1zIGluIGEgZ2l2ZW4gY2F0ZWdvcnkuIE5vdGljZSBoZXJlIHRoYXQgeW91IGhhdmUgdG8gdXNlIHF1b3RhdGlvbiBtYXJrcyB0byBzaG93IHlvdSdyZSBtYXRjaGluZyBhIGNoYXJhY3RlciBzdHJpbmcuDQpMb29rIGF0IHRoZSBlcnJvciBiZWxvdzoNCmBgYHtyfQ0Kc3ByICU+JSANCiAgZmlsdGVyKHdvcmQgPT0gcmVsYXRpdmUpDQpgYGANCg0KVGhlIGNvcnJlY3Qgc3ludGF4IGlzOiAoYmVjYXVzZSB5b3UncmUgbWF0Y2hpbmcgdG8gYSBzdHJpbmcpDQpgYGB7cn0NCnNwciAlPiUgDQogIGZpbHRlcih3b3JkID09ICJyZWxhdGl2ZSIpDQpgYGANCg0KWW91IGNhbiBhbHNvIHVzZSBmaWx0ZXIgdG8gZWFzaWx5IGRyb3Agcm93cy4gTGV0J3MgZHJvcCBhbGwgcHJhY3RpY2Ugcm93cyBhbmQgc2F2ZSB0aGUgb3V0cHV0LiANCmBgYHtyfQ0Kc3ByIDwtIHNwciAlPiUgDQogIGZpbHRlcihjb25kaXRpb24gIT0gInByYWN0aWNlIikNCmBgYA0KDQpUbyB1c2UgJWluJSwgZ2l2ZSBhbiBhcnJheSBvZiBvcHRpb25zIChmb3JtYXR0ZWQgaW4gdGhlIGNvcnJlY3Qgd2F5IGJhc2VkIG9uIHdoZXRoZXIgdGhlIGNvbHVtbiBpcyBhIGNoYXJhY3RlciBvciBudW1lcmljKToNCmBgYHtyfQ0Kc3ByICU+JSANCiAgZmlsdGVyKHdvcmQgJWluJSBjKCJjYXQiLCAiZG9nIikpDQoNCnNwciAlPiUgDQogIGZpbHRlcihzZW50ZW5jZV9udW0gJWluJSBjKDIsIDQpKQ0KYGBgDQpOb3RlIHRoYXQgZmlsdGVyIGlzIGNhc2Utc2Vuc2l0aXZlLCBzbyBjYXBpdGFsaXphdGlvbiBtYXR0ZXJzLg0KDQpXZSBjYW4gYWxzbyBzcGVjaWZ5IHNldmVyYWwgY29uZGl0aW9ucyBpbiBvbmUgYGZpbHRlcigpYCBjYWxsLCBlLmcuIA0KYGBge3J9DQpzcHIgJT4lIA0KICBmaWx0ZXIod29yZCA9PSAicmVsYXRpdmUiICYgUlQgPiAyMDApDQoNCnNwciAlPiUgDQogIGZpbHRlcihSVCA+IDMwMCB8IFJUIDwgMTUwKQ0KYGBgDQoNCldlIGNhbiBhbHNvICJjaGFpbiIgZGlmZmVyZW50IGZ1bmN0aW9ucywgd2hpY2ggaXMgb25lIG9mIHRoZSB0aGluZ3MgdGhhdCBtYWtlcyB0aGUgcGlwZSBzbyB1c2VmdWwuIEZvciBleGFtcGxlLCB3ZSBjb3VsZCBmaWx0ZXIgZm9yIGRhdGEgaW4gY29uZGl0aW9uIEIsIHdoaWNoIGlzIGFuIGluY29tcGxldGUgc2VudGVuY2UsIGFuZCBvbmx5IGxvb2sgYXQgdGhlIHdvcmRzIGFuZCB0aGVpciByZXNwb25zZSB0aW1lczoNCmBgYHtyfQ0Kc3ByICU+JSANCiAgZmlsdGVyKGNvbmRpdGlvbiA9PSAiY29uZF9CX2RvZyIpICU+JSANCiAgc2VsZWN0KHdvcmQsIFJUKQ0KYGBgDQoNCk9uZSB1c2VmdWwgZnVuY3Rpb24gdGhhdCBjYW4gYmUgY2hhaW5lZCB0byBgZmlsdGVyKClgIGlzIGBkaXN0aW5jdCgpYCwgd2hpY2ggd2lsbCByZXR1cm4gb25seSB0aGUgdW5pcXVlIHJvd3MuIFdpdGhvdXQgYW4gYXJndW1lbnQsIGl0IHJldHVybnMgYWxsIHJvd3MgdGhhdCBhcmUgdW5pcXVlIGluIGFsbCBjb2x1bW5zLiANCg0KWW91IGNhbiBhbHNvIGFkZCBhIGNvbHVtbiBuYW1lIGFzIGFuIGFyZ3VtZW50IHRvIHJldHVybiBvbmx5IHRoZSB1bmlxdWUgdmFsdWVzIGluIGEgY2VydGFpbiBjb2x1bW4gKHVzZWZ1bCB3aXRoIGZhY3RvcnMpDQoNCmBgYHtyfQ0Kc3ByICU+JSANCiAgZmlsdGVyKGNvbmRpdGlvbiA9PSAiY29uZF9CX2RvZyIpICU+JSANCiAgZGlzdGluY3Qoc2VudGVuY2UpDQpgYGANCg0KWW91IGNhbiBhbHNvIHVzZSBpdCBvbiBpdHMgb3duIHRvIHJldHVybiB1bmlxdWUgdmFsdWVzIG9yIGNvbWJpbmF0aW9ucyBvZiB2YWx1ZXMuDQoNCmBgYHtyfQ0Kc3ByICU+JSANCiAgZGlzdGluY3Qoc2VudGVuY2UsIGNvbmRpdGlvbikNCmBgYA0KDQoNCiMgKDcpIFNlcGFyYXRpbmcgYW5kIHVuaXRpbmcgY29sdW1ucw0KDQpGb3IgdGhlIG5leHQgdHdvIGV4YW1wbGVzLCB3ZSdsbCB1c2UgYSBkaWZmZXJlbnQgZGF0YXNldCBjYWxsZWQgYGFuaW1hbF9jb3JwdXNgLiBSZWFkIGl0IGluIGFuZCBmYW1pbGlhcmlzZSB5b3Vyc2VsZiB3aXRoIGl0cyBjb250ZW50cy4NCmBgYHtyfQ0KYW5pbWFsX2NvcnB1cyA8LSByZWFkX2NzdjIoImRhdGEvY2F0X2RvZ19jb3JwdXNfZGF0YS5jc3YiKQ0KYGBgDQoNClRoaXMgY29udGFpbnMgY29ycHVzIGRhdGEgb2YgImNhdCIgYW5kICJkb2ciIHRvZ2V0aGVyIHdpdGggdGhlIHdvcmRzIHRoYXQgcHJlY2VkZSAiY2F0IiBhbmQgImRvZyIuIFRoZXNlIGFyZSBjYWxsZWQgImNvbGxvY2F0ZXMiIGFuZCwgaW4gb3VyIGV4YW1wbGUsIGFsc28gaW5jbHVkZSBwYXJ0IG9mIHNwZWVjaCB0YWdzICh0aGUgZm9ybWF0IGlzIHdvcmRfdGFnKS4NCg0KVGhpcyBkYXRhIGlzIG5vdCB0aWR5LiBXaHk/DQoNClRoZSBjb2xsb2NhdGVzIGNvbHVtbiBjb250YWlucyB0d28gdmFyaWFibGVzICh3b3JkIGFuZCB0YWcpIGFsdGhvdWdoIGFjY29yZGluZyB0byB0aWR5IGRhdGEgcHJpbmNpcGxlcywgZWFjaCB2YXJpYWJsZSBzaG91bGQgYmUgc2F2ZWQgaW4gaXRzIG93biBjb2x1bW4uDQoNCiMjIHNlcGFyYXRlKCkNCg0KTHVja2lseSwgdGhlIHRpZHl2ZXJzZSBoYXMgYSBjb21tYW5kIGZvciB0aGF0OiBgc2VwYXJhdGUoKWAuIEl0IHRha2VzIHRoZSBmb2xsb3dpbmcgYXJndW1lbnRzOg0KLSBkYXRhOiBvdXIgZGF0YWZyYW1lLCB3ZSdsbCBwaXBlIGl0DQotIGNvbDogd2hpY2ggY29sdW1uIG5lZWRzIHRvIGJlIHNlcGFyYXRlZA0KLSBpbnRvOiBhIHZlY3RvciB0aGF0IGNvbnRhaW5zIHRoZSBuYW1lcyBvZiB0aGUgbmV3IGNvbHVtbnMNCi0gc2VwOiB3aGljaCBzeW1ib2wgc2VwYXJhdGVzIHRoZSB2YWx1ZXMNCi0gcmVtb3ZlIChvcHRpb25hbCk6IGJ5IGRlZmF1bHQsIHRoZSBvcmlnaW5hbCBjb2x1bW4gd2lsbCBiZSBkZWxldGVkLiBTZXQgYHJlbW92ZWAgdG8gRkFMU0UgdG8ga2VlcCBpdC4NCg0KYGBge3J9DQooYW5pbWFsX2NvcnB1cyA8LSBhbmltYWxfY29ycHVzICU+JSANCiAgc2VwYXJhdGUoY29sID0gY29sbG9jYXRlLCANCiAgICAgICAgICAgaW50byA9IGMoImNvbGwiLCAiY29sbF90YWciKSwgDQogICAgICAgICAgIHNlcCA9ICJfIikpDQpgYGANCg0KQW5vdGhlciBleGFtcGxlOiBJbiB0aGUgU1BSIGRhdGEsIHRoZSBjb25kaXRpb24gY29sdW1uIGNvbnRhaW5zIHR3byBwaWVjZXMgb2YgaW5mb3JtYXRpb246IHdoaWNoIGNvbmRpdGlvbiB0aGUgaXRlbSB3YXMgaW4gKGNvbmRBIG9yIGNvbmRCKSBhbmQgd2hpY2ggYW5pbWFsIHdhcyBiZWluZyByZWFkIGFib3V0IChjYXQgb3IgZG9nKQ0KYGBge3J9DQooc3ByIDwtIHNwciAlPiUgDQogIHNlcGFyYXRlKGNvbmRpdGlvbiwNCiAgICAgICAgICAgaW50byA9IGMoImNvbmRpdGlvbiIsICJhbmltYWwiKSwNCiAgICAgICAgICAgc2VwID0gIl8iKSkNCmBgYA0KDQoNCiMjIHVuaXRlKCkNCg0KVGhlIG9wcG9zaXRlIG9mIGBzZXBhcmF0ZSgpYC4gVGhpcyBsZXRzIHlvdSBnbHVlIGNvbHVtbnMgdG9nZXRoZXIuIA0KLSBjb2wgaXMgdGhlIG5hbWUgb2YgdGhlIG5ldyBjb2x1bW4NCi0gdGhlIG5leHQgYXJndW1lbnQsIGEgdmVjdG9yLCBsaXN0cyB0aGUgY29sdW1ucyB0aGF0IHNob3VsZCBiZSB1bml0ZWQNCi0gc2VwLCBhcyBhYm92ZSwgbGV0cyB5b3Ugc3BlY2lmeSBob3cgdGhlIHZhbHVlcyBzaG91bGQgYmUgc2VwYXJhdGVkDQoNCkxldCdzIHNheSB3ZSdkIGxpa2Ugb3VyIGRhdGEgdG8gYmUgaW4gdGhlIGZvcm1hdCAiY29sbG9jYXRlIGNhdC9kb2ciLCBzbyB3aXRob3V0IHRoZSB0YWcsIGJ1dCBpbiBvbmUgY29sdW1uLCBzZXBhcmF0ZWQgYnkgYSBzcGFjZS4gDQpgYGB7cn0NCmFuaW1hbF9jb3JwdXMgJT4lIA0KICB1bml0ZShjb2wgPSAiY29sbF93b3JkIiwNCiAgICAgICAgYygiY29sbCIsICJhbmltYWwiKSwNCiAgICAgICAgc2VwID0gIiAiKQ0KYGBgDQoNClRoaXMgbGVhZHMgdG8gYSBsb3Qgb2YgcmVwZXRpdGlvbiAtIHNvbWUgYmlncmFtcyBhcHBlYXIgc2V2ZXJhbCB0aW1lcyBpbiBqdXN0IG91ciBwcmV2aWV3LiBUbyByZW1lZHkgdGhpcywgd2UgY2FuIHVzZSBgZGlzdGluY3QoKWAsIHdoaWNoIG9ubHkga2VlcHMgdW5pcXVlIHJvd3MuIFRvIG1ha2UgY2xlYXIgdGhhdCB3ZSB3YW50IHVuaXF1ZSBjb2xsb2NhdGUgY2F0L2RvZyBjb21iaW5hdGlvbnMsIHdlIGNhbiBwdXQgYGNvbGxfd29yZGAgaW50byBgZGlzdGluY3QoKWAgdG8gbWFrZSBjbGVhciB0aGF0IHRoaXMgaXMgdGhlIHJlbGV2YW50IGNvbHVtbiBhbmQgdGFncyBzaG91bGQgYmUgaWdub3JlZC4NCmBgYHtyfQ0KYW5pbWFsX2NvcnB1cyAlPiUgDQogIHVuaXRlKGNvbCA9ICJjb2xsX3dvcmQiLA0KICAgICAgICBjKCJjb2xsIiwgImFuaW1hbCIpLA0KICAgICAgICBzZXAgPSAiICIpICU+JSANCiAgZGlzdGluY3QoY29sbF93b3JkKQ0KYGBgDQoNCiMgKDgpIENyZWF0aW5nIGFuZCBjaGFuZ2luZyBjb2x1bW5zIHdpdGggbXV0YXRlKCkNCg0KV2l0aCB0aGUgYG11dGF0ZSgpYCBmdW5jdGlvbiwgeW91IGNhbiBjaGFuZ2UgZXhpc3RpbmcgY29sdW1ucyBhbmQgYWRkIG5ldyBvbmVzLiANClRoZSBzeW50YXggaXM6DQptdXRhdGUoZGF0YSwgY29sX25hbWUgPSBzb21lX29wZXJhdGlvbikNCm9yLCB3aXRoIHRoZSBwaXBlOiANCmRhdGEgJT4lIG11dGF0ZShjb2xfbmFtZSA9IHNvbWVfb3BlcmF0aW9uKS4NCg0KIVtNdXRhdGVdKGltZy9kcGx5cl9tdXRhdGUucG5nKXt3aWR0aD01MCV9DQoNClRoZSByZXNwb25zZSB0aW1lcyBhcmUgbWVhc3VyZWQgaW4gbXMuIExldCdzIGNvbnZlcnQgdGhlbSB0byBzZWNvbmRzIGJ5IGRpdmlkaW5nIGJ5IDEwMDA6DQpgYGB7cn0NCihzcHIgPC0gc3ByICU+JSANCiAgbXV0YXRlKFJUX3MgPSBSVCAvIDEwMDApKQ0KYGBgDQpOb3csIHRoZXJlJ3MgYSBuZXcgY29sdW1uIGNhbGxlZCBSVF9zIChpdCdzIGF0IHRoZSB2ZXJ5IGVuZCBieSBkZWZhdWx0KS4NCg0KWW91IGNhbiBhbHNvIHNhdmUgdGhlIG5ldyBjb2x1bW4gd2l0aCB0aGUgc2FtZSBuYW1lLCBhbmQgdGhpcyB3aWxsIHVwZGF0ZSBhbGwgdGhlIGl0ZW1zIGluIHRoYXQgY29sdW1uIChzZWUgYmVsb3csIHdoZXJlIEkgZGl2aWRlIHJlc3BvbnNlIHRpbWVzIGJ5IDEwMDAsIGJ1dCBub3RlIHRoYXQgSSBkb24ndCBzYXZlIHRoZSBvdXRwdXQpOg0KYGBge3J9DQpzcHIgJT4lIA0KICBtdXRhdGUoUlQgPSBSVCAvIDEwMDApDQpgYGANCg0KWW91IGNhbiBhbHNvIGRvIG9wZXJhdGlvbnMgdG8gY2hhcmFjdGVyIGNvbHVtbnMgLSBmb3IgZXhhbXBsZTogDQpgYGB7cn0NCnNwciAlPiUgDQogIG11dGF0ZSh3b3JkID0gdG9sb3dlcih3b3JkKSkNCmBgYA0KDQoNCiMjIENoYW5nZSBkYXRhIHR5cGUgaW4gYSBjb2x1bW4NCg0KV2UgY2FuIGFsc28gY2hhbmdlIGRhdGEgdHlwZXMgdXNpbmcgYG11dGF0ZSgpYC4gSW5zdGVhZCBvZiB0aGUgY29kZSB3ZSB1c2VkIGVhcmxpZXIgdG8gY29udmVydCBwYXJ0aWNpcGFudCBhbmQgY29uZGl0aW9uIHRvIGZhY3RvcnMsIHdlIGNvdWxkIHdyaXRlOiANCmBgYHtyfQ0KKHNwciA8LSBzcHIgJT4lIA0KICBtdXRhdGUocGFydGljaXBhbnQgPSBhc19mYWN0b3IocGFydGljaXBhbnQpLA0KICAgICAgICAgY29uZGl0aW9uID0gYXNfZmFjdG9yKGNvbmRpdGlvbikpKQ0KYGBgDQpBcyB5b3UgY2FuIHNlZSwgd2UgY2FuIGNoYW5nZSBzZXZlcmFsIHZhcmlhYmxlcyB3aXRoaW4gb25lIGBtdXRhdGUoKWAgIGNhbGwuIEluIHRoZSBzYW1lIHdheSwgd2UgY291bGQgY3JlYXRlIHNldmVyYWwgbmV3IGNvbHVtbnMgYXQgdGhlIHNhbWUgdGltZS4gSGVyZSwgZHJvcHBpbmcgZWFjaCBuZXcgY29sdW1uIG9udG8gYSBuZXcgbGluZSBpcyBjb25zaWRlcmVkIGdvb2Qgc3R5bGUgYW5kIG1ha2VzIHRoZSBjb2RlIG1vcmUgcmVhZGFibGUgKGJ1dCBpdCdzIG5vdCBuZWNlc3NhcnkpLg0KDQojIyBSZWxhYmVsIGZhY3RvcnMNCkluIG91ciBTUFIgZXhwZXJpbWVudCwgY29uZGl0aW9uIEEgcmVwcmVzZW50cyBhIG1hdGNoIChpLmUuIGNhdC9kb2cgaXMgcHJlc2VudGVkIHdpdGggYSBtYXRjaGluZyBjb2xsb2NhdGU6IHB1cnJpbmcgY2F0LCBndWlkZSBkb2cpIGFuZCBjb25kaXRpb24gQiBpcyBhIG1pc21hdGNoIChlLmcuIGd1aWRlIGNhdCwgcHVycmluZyBkb2cpLiBUbyBtYWtlIHRoaXMgY2xlYXIgaW4gdGhlIGRhdGEsIHdlIHNob3VsZCBsYWJlbCB0aGlzIGV4cGxpY2l0bHkuIFdpdGhpbiBhIGBtdXRhdGUoKWAgY29tbWFuZCwgd2UgY2FuIHVzZSBgcmVjb2RlKClgIHRvIGNoYW5nZSB0aGUgZmFjdG9yIGxhYmVscy4gVGhlIGZvcm1hdCBmb3IgdGhpcyBpcyBvbGQgbGFiZWwgPSAibmV3IGxhYmVsIi4NCmBgYHtyfQ0KKHNwciA8LSBzcHIgJT4lIA0KICBtdXRhdGUoY29uZGl0aW9uID0gcmVjb2RlKGNvbmRpdGlvbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uZEEgPSAibWF0Y2giLCBjb25kQiA9ICJtaXNtYXRjaCIpKSkNCmBgYA0KDQpMZXQncyBsb29rIGF0IG91ciB0aGlyZCBkYXRhc2V0LCBhbmltYWxfc3VydmV5LiBJdCBjb250YWlucyBkYXRhIGZyb20gdGhlIHNhbWUgcGFydGljaXBhbnRzIHdobyBhbnN3ZXJlZCBhIGZldyBzb2Npb2RlbW9ncmFwaGljIHF1ZXN0aW9ucyBhbmQgYWxzbyBpbmRpY2F0ZWQgaG93IGN1dGUgdGhleSB0aGluayBhbmltYWxzIGFyZSwgaG93IG11Y2ggdGhleSgnZCkgbGlrZSBsb29rIGF0LCBwZXQsIGFuZCBvd24gYW4gYW5pbWFsIChzY2FsZTogMS01KS4gQmVzaWRlcyB0aGF0LCB0aGV5IHdlcmUgYWxzbyBhc2tlZCB0byByYXRlIGNhdHMgYW5kIGRvZ3MgKHNjYWxlOiAxLTcpLg0KYGBge3J9DQphbmltYWxfc3VydmV5IDwtIHJlYWRfY3N2KCJhbmltYWxfc3VydmV5LmNzdiIpDQpgYGANCg0KSW4gb3VyIGFuaW1hbF9zdXJ2ZXkgZGF0YSwgZWR1Y2F0aW9uIGlzIHJlcHJlc2VudGVkIGJ5IG51bWVyaWMgY29kZXMsIDEtNC4gV2UgY291bGQgdHVybiB0aGVzZSBpbnRvIGxhYmVscyBsaWtlIHNvOg0KYGBge3J9DQooYW5pbWFsX3N1cnZleSA8LSBhbmltYWxfc3VydmV5ICU+JSANCiAgbXV0YXRlKGVkdWNhdGlvbiA9IHJlY29kZShlZHVjYXRpb24sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIjEiID0gImVsZW1lbnRhcnkgc2Nob29sIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMiIgPSAiaGlnaCBzY2hvb2wiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICIzIiA9ICJCYWNoZWxvcidzIGRlZ3JlZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIjQiID0gIk1hc3RlcidzIGRlZ3JlZSBvciBoaWdoZXIiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpDQpgYGANCkJlY2F1c2UgdGhlc2UgbnVtYmVycyBzaG91bGQgYmUgdHJlYXRlZCBhcyBjaGFyYWN0ZXJzLCB3ZSBuZWVkIHRvIHB1dCB0aGVtIGluIHF1b3RhdGlvbiBtYXJrcyENCg0KDQojIyBJZi1lbHNlLXN0YXRlbWVudHMNCg0KTm93IGZvciBzb21ldGhpbmcgZmFuY3kuIFlvdSBjYW4gYWxzbyBtYWtlIG5ldyBjb2x1bW5zIGJhc2VkIG9uICJpZiIgY29uZGl0aW9ucyB1c2luZyB0aGUgY2FsbCBgaWZlbHNlKClgLiBUaGUgc3ludGF4IG9mIGlmZWxzZSBpczogaWZlbHNlKHRoaXNfaXNfdHJ1ZSwgdGhpc19oYXBwZW5zLCBlbHNlX3RoaXNfaGFwcGVucykuIEZvciBleGFtcGxlLCB3ZSBjb3VsZCBjcmVhdGUgYSBjb2x1bW4gY2FsbGVkICJSVF9zaG9ydCIgdGhhdCBjb250YWlucyAic2hvcnQiIGlmIHRoZSByZXNwb25zZSB0aW1lIGlzIGZhc3RlciB0aGFuIDEwMCBtcyBhbmQgImxvbmciIGlmIGl0IGlzbid0Og0KDQpgYGB7cn0NCnNwciAlPiUgDQogIG11dGF0ZShSVF9zaG9ydCA9IGlmZWxzZShSVCA8IDEwMCwgInNob3J0IiwgImxvbmciKSkgJT4lIA0KICBzZWxlY3QoUlQsIFJUX3Nob3J0KQ0KYGBgDQoNCllvdSBjYW4gYWxzbyB1c2UgaWZlbHNlIG9uIGNhdGVnb3JpY2FsIC8gY2hhcmFjdGVyIGNvbHVtbnM6DQpgYGB7cn0NCihzcHIgPC0gc3ByICU+JSANCiAgbXV0YXRlKHBvc2l0aW9uID0gaWZlbHNlKHdvcmQgJWluJSBjKCJjYXQiLCAiZG9nIiksICJjcml0aWNhbCIsICJub3QgY3JpdGljYWwiKSkpDQpgYGANCg0KDQojIyBTZXZlcmFsIGNvbmRpdGlvbnM6IGNhc2Vfd2hlbigpDQpXaGF0IGlmIHlvdSBoYXZlIHNldmVyYWwgY29uZGl0aW9ucz8gRm9yIGV4YW1wbGUsIGlmIFJUcyBhcmUgc2hvcnRlciB0aGFuIDEwMCBtcywgdGhleSBzaG91bGQgYmUgbGFiZWxsZWQgInNob3J0IiwgaWYgdGhleSdyZSBsb25nZXIgdGhhbiA1MDAgbXMsICJsb25nIiwgYW5kICJub3JtYWwiIGZvciBhbGwgb3RoZXIgUlRzLiBXaGlsZSBpdCdzIHBvc3NpYmxlIHRvIGNoYWluIHNldmVyYWwgYGlmX2Vsc2UoKWAgc3RhdGVtZW50cywgaXQgZ2V0cyBjb25mdXNpbmcgYW5kIGhhcmQgdG8gcmVhZC4gSW5zdGVhZCwgd2Ugc2hvdWxkIHVzZSBgY2FzZV93aGVuKClgLg0KDQohW0Nhc2Ugd2hlbjogYW4gZXh0ZW5zaW9uIG9mIGlmLWVsc2VdKGltZy9kcGx5cl9jYXNlX3doZW4ucG5nKXt3aWR0aD01MCV9DQoNClRoZSBzeW50YXggd2l0aGluIGBjYXNlX3doZW4oKWAgaXM6DQpjb25kaXRpb24gfiB3aGF0IHRvIGRvIGlmIGl0IGlzIHRydWUgKGNhbiBiZSB1c2VkIGFzIG9mdGVuIGFzIHlvdSB3YW50IGFuZCBjYW4gZXZlbiByZWZlciB0byBkaWZmZXJlbnQgdmFyaWFibGVzISksDQpUUlVFIH4gd2hhdCB0byBkbyBpbiBhbGwgb3RoZXIgY2FzZXMuDQpgYGB7cn0NCnNwciAlPiUgDQogIG11dGF0ZShSVF9jYXRlZ29yeSA9IGNhc2Vfd2hlbigNCiAgICBSVCA8IDEwMCB+ICJzaG9ydCIsDQogICAgUlQgPiA1MDAgfiAibG9uZyIsDQogICAgVFJVRSB+ICJub3JtYWwiDQogICkpICU+JSANCiAgc2VsZWN0KFJULCBSVF9jYXRlZ29yeSkNCmBgYA0KDQoNCkFub3RoZXIgZXhhbXBsZTogSW4gdGhlIHF1ZXN0aW9ubmFpcmUsIHBhcnRpY2lwYW50cyB3ZXJlIGFza2VkIHRvIGluZGljYXRlIGhvdyBtdWNoIHRoZXkgbGlrZSBjYXRzIGFuZCBkb2dzLCByZXNwZWN0aXZlbHkuIFdlIGNhbiBjb252ZXJ0IHRoaXMgaW5mb3JtYXRpb24gaW50byBhIGNhdGVnb3J5LiBUbyBjcmVhdGUgYSB2YXJpYWJsZSBjYWxsZWQgInByZWZlcmVuY2UiLCB3ZSBjYW4gdXNlIHRoZSBmb2xsb3dpbmcgYGNhc2Vfd2hlbigpYCBzdGF0ZW1lbnQ6DQpgYGB7cn0NCihhbmltYWxfc3VydmV5IDwtIGFuaW1hbF9zdXJ2ZXkgJT4lIA0KICBtdXRhdGUocHJlZmVyZW5jZSA9IGNhc2Vfd2hlbigNCiAgICBjYXRzID4gZG9ncyB+ICJjYXRzIiwNCiAgICBkb2dzID4gY2F0cyB+ICJkb2dzIiwNCiAgICBUUlVFIH4gInVuZGVjaWRlZCINCiAgKSkpDQpgYGANCklmIHdlIGxvb2sgYXQgdGhlIG91dHB1dCwgd2Ugc2VlIHRoYXQgdGhlICJlbHNlL1RSVUUiIGNvbmRpdGlvbiB3YXNuJ3QgbmVjZXNzYXJ5LCBzdHJpY3RseSBzcGVha2luZywgYnV0IGl0J3MgZ29vZCB0byBhY2NvdW50IGZvciBhbGwgdGhlIHBvdGVudGlhbCBjYXNlcyBpbiB0aGUgZGF0YSBzbyB5b3UgZG9uJ3QgZW5kIHVwIHdpdGggc3VwZXJmbHVvdXMgTkFzLg0KDQoNCiMgKDkpIFN1bW1hcnkgdGFibGVzIHdpdGggZ3JvdXBpbmdzDQoNCiMjIHN1bW1hcml6ZSgpDQoNCkxldCdzIGV4dHJhY3Qgc29tZSBpbmZvcm1hdGlvbiBvbiB0aGUgcmVhY3Rpb24gdGltZXMgaW4gdGhlIHNlbGYtcGFjZWQgcmVhZGluZyBkYXRhLiBJZiB3ZSB3YW50IHRvIGRvIHRoaXMgdGhlIHRpZHkgd2F5LCB3ZSBjYW4gdXNlIGBzdW1tYXJpc2Uob3BlcmF0aW9uKHZhcmlhYmxlKSlgLg0KDQpZb3UgY2FuIHVzZSBtdWx0aXBsZSBkaWZmZXJlbnQgb3BlcmF0aW9ucyBpbiB0aGUgc3VtbWFyaXplIHBhcnQsIGluY2x1ZGluZzoNCi0gbWVhbihjb2xfbmFtZSkNCi0gbWVkaWFuKGNvbF9uYW1lKQ0KLSBtYXgoY29sX25hbWUpDQotIG1pbihjb2xfbmFtZSkNCg0KU28gdG8gZ2V0IHRoZSBhdmVyYWdlIFJUczoNCmBgYHtyfQ0Kc3ByICU+JSANCiAgc3VtbWFyaXNlKG1lYW4oUlQpKQ0KYGBgDQoNCi4uLmFuZCB0aGUgbWVkaWFuIFJUczoNCmBgYHtyfQ0Kc3ByICU+JSANCiAgc3VtbWFyaXNlKG1lZGlhbihSVCkpDQpgYGANCg0KQnkgZGVmYXVsdCwgdGhlIGNvbHVtbiBpcyBsYWJlbGxlZCAibWVhbihSVCkiIG9yICJtZWRpYW4oUlQpIiwgcmVzcGVjdGl2ZWx5LiBXZSBjYW4gc2V0IG91ciBvd24gbmFtZXMsIHRob3VnaDoNCmBgYHtyfQ0Kc3ByICU+JSANCiAgc3VtbWFyaXNlKGF2ZXJhZ2VfUlQgPSBtZWFuKFJUKSkNCmBgYA0KDQoNCiMjIGdyb3VwX2J5KCkgDQpJbiBvdXIgU1BSIGV4cGVyaW1lbnQsIHdlIHNob3dlZCBtYXRjaGluZyBhbmQgbWlzbWF0Y2hpbmcgY29sbG9jYXRlICsgY2F0L2RvZyBjb21iaW5hdGlvbnMsIHNvIHdlIG1pZ2h0IGFzc3VtZSB0aGF0IHRoZSBtaXNtYXRjaGVkIGNvbWJpbmF0aW9ucyB3ZXJlIHJlYWQgbW9yZSBzbG93bHkuIFdlJ2QgbGlrZSB0byBzZWUgdGhlIGF2ZXJhZ2UgUlRzIGZvciBlYWNoIGNvbmRpdGlvbi4NCg0KVG8gbG9vayBhdCBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIHNwZWNpZmljIGdyb3VwaW5ncyBsaWtlIHRoZXNlLCB3ZSBoYXZlIHRvIHVzZSBhIHR3by0gKG1vcmUgbGlrZSB0aHJlZS0pIHN0ZXAgcHJvY2Vzcy4gDQoNCkZpcnN0LCBncm91cCBieSB5b3VyIGdyb3VwaW5nIHZhcmlhYmxlIChoZXJlOiBjb25kaXRpb24pIHVzaW5nIGBncm91cF9ieSgpYA0KVGhlbiwgc3VtbWFyaXplLCB3aGljaCBjcmVhdGVzIGEgY29sdW1uIGJhc2VkIG9uIGEgdHJhbnNmb3JtYXRpb24gdG8gYW5vdGhlciBjb2x1bW4sIHVzaW5nIGBzdW1tYXJpemUoKWAgb3IgYHN1bW1hcmlzZSgpYA0KRmluYWxseSwgdW5ncm91cCAoc28gdGhhdCBSIGZvcmdldHMgdGhhdCB0aGlzIGlzIGEgZ3JvdXBpbmcgYW5kIGNhcnJpZXMgb24gYXMgbm9ybWFsLCB3aXRoIGB1bmdyb3VwKClgDQpVc3VhbGx5IHRoaXMgbWFrZXMgbm8gZGlmZmVyZW5jZSB0byB3aGF0IHlvdSBzZWUgYnV0IGlzIGFuIGltcG9ydGFudCBmYWlsLXNhZmUgaWYgeW91J3JlIGNvbnRpbnVpbmcgaW4gdGhlIGNvZGUgYmxvY2suIEZvciBleGFtcGxlLCBpZiB5b3UgbmVlZCB0byBkcm9wIHRoZSBmb3JtZXIgZ3JvdXBpbmcgdmFyaWFibGUgYW5kIGRvbid0IHVzZSBgdW5ncm91cCgpYCwgUiB3aWxsIHJlZnVzZSB0byBkcm9wIGl0Lg0KDQohW0dyb3VwIGFuZCB1bmdyb3VwXShpbWcvZ3JvdXBfYnlfdW5ncm91cC5wbmcpe3dpZHRoPTUwJX0NCg0KRm9yIGV4YW1wbGUsIHRvIHJldHVybiB0aGUgYXZlcmFnZSBSVCBpbiBlYWNoIGNvbmRpdGlvbjoNCmBgYHtyfQ0Kc3ByICU+JSANCiAgZ3JvdXBfYnkoY29uZGl0aW9uKSAlPiUgDQogIHN1bW1hcml6ZShtZWFuKFJUKSkgJT4lIA0KICB1bmdyb3VwKCkNCmBgYA0KDQpZb3UgY2FuIGFsc28gZ2l2ZSBhIG5hbWUgdG8geW91ciBuZXcgc3VtbWFyeSBjb2x1bW46IA0KYGBge3J9DQpzcHIgJT4lIA0KICBncm91cF9ieShjb25kaXRpb24pICU+JSANCiAgc3VtbWFyaXplKGF2ZXJhZ2VfUlQgPSBtZWFuKFJUKSkgJT4lIA0KICB1bmdyb3VwKCkNCmBgYA0KDQpPciBmdXJ0aGVyIG1hbmlwdWxhdGUgaXQsIGUuZy4gY29udmVydGluZyBtcyB0byBzZWNvbmRzOg0KYGBge3J9DQpzcHIgJT4lIA0KICBncm91cF9ieShjb25kaXRpb24pICU+JSANCiAgc3VtbWFyaXplKGF2ZXJhZ2VfUlRfcyA9IG1lYW4oUlQpIC8gMTAwMCkgJT4lIA0KICB1bmdyb3VwKCkNCmBgYA0KDQojIyBjb3VudCgpDQpGb3IgY2F0ZWdvcmljYWwgY29sdW1ucywgeW91IGNhbiBhbHNvIGNvdW50IGhvdyBtYW55IHJvd3MgYXJlIGluIGVhY2ggY2F0ZWdvcnkgdXNpbmcgYGNvdW50KClgIGluc3RlYWQgb2YgYHN1bW1hcml6ZSgpYC4gSGVyZSwgd2UncmUgY291bnRpbmcgaG93IG9mdGVuIHdoaWNoIHdvcmQgYXBwZWFyZWQgaW4gdGhlIGV4cGVyaW1lbnQuIFRoZSBjb3VudCBpcyBkaXNwbGF5ZWQgaW4gdGhlIG5ldyAibiIgY29sdW1uOg0KYGBge3J9DQpzcHIgJT4lIA0KICBncm91cF9ieSh3b3JkKSAlPiUgDQogIGNvdW50KCkgJT4lIA0KICB1bmdyb3VwKCkNCmBgYA0KDQpUaGlzIGlzIHNvcnRlZCBhbHBoYWJldGljYWxseSwgYnV0IHdlIGNhbiBwaXBlIGl0IGludG8gdGhlIGBhcnJhbmdlKClgIGNhbGwgd2UgZGlzY3Vzc2VkIGVhcmxpZXIgdG8gc29ydCBieSBmcmVxdWVuY3ksIGluIGRlc2NlbmRpbmcgb3JkZXIuDQpgYGB7cn0NCnNwciAlPiUgDQogIGdyb3VwX2J5KHdvcmQpICU+JSANCiAgY291bnQoKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGFycmFuZ2UoZGVzYyhuKSkNCmBgYA0KDQpZb3UgY2FuIGFsc28gZ3JvdXAgYnkgbW9yZSB0aGFuIG9uZSBjb2x1bW4gdG8gcmV0dXJuIHRoZSB1bmlxdWUgY29tYmluYXRpb25zIG9mIHRoZSB2YXJpYWJsZXMuDQpgYGB7cn0NCnNwciAlPiUgDQogIGdyb3VwX2J5KGNvbmRpdGlvbiwgYW5pbWFsKSAlPiUgDQogIHN1bW1hcml6ZShtZWFuKFJUKSkNCmBgYA0KDQoNCiMgKDEwKSBhY3Jvc3MoKQ0KDQpgYWNyb3NzKClgIGlzIGEgaGVscGVyIGZvciBgbXV0YXRlKClgIGFuZCBgc3VtbWFyaXNlYC4gSXQgbGV0cyB5b3UgZWFzaWx5IGFwcGx5IGEgY2hhbmdlIG9yIGNyZWF0ZSBhIHN1bW1hcnkgZm9yIHNldmVyYWwgdmFyaWFibGVzIGJlY2F1c2UgeW91IGNhbiB1c2UgYHNlbGVjdCgpYCBzZW1hbnRpY3MgKGBzdGFydHNfd2l0aCgpYCwgYGVuZHNfd2l0aCgpYCwgYGNvbnRhaW5zKClgLCBldGMuKSB3aXRoIGBhY3Jvc3MoKWAuDQoNCkZvciBleGFtcGxlLCBpZiB3ZSdkIGxpa2UgdG8gc2VlIHRoZSBtZWFuIGZvciBjb2x1bW5zIHRoYXQgZW5kIHdpdGggIm51bSI6DQpgYGB7cn0NCnNwciAlPiUgDQogIHN1bW1hcmlzZShhY3Jvc3MoZW5kc193aXRoKCJudW0iKSwgbWVhbikpDQpgYGANCg0KVGhpcyBhbHNvIHdvcmtzIHdpdGggYSBsaXN0IG9mIGZ1bmN0aW9ucywgZS5nLiB0aGUgbWVhbiBhbmQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbjoNCmBgYHtyfQ0Kc3ByICU+JSANCiAgc3VtbWFyaXNlKGFjcm9zcyhlbmRzX3dpdGgoIm51bSIpLCBjKG1lYW4sIHNkKSkpDQpgYGANCg0KSGVyZSdzIGFuIGV4YW1wbGUgb2YgYGFjcm9zcygpYCBpbiBhIGBtdXRhdGUoKWAgZnVuY3Rpb24uIFRoaXMgbGV0cyB5b3UgZWFzaWx5IGNvbnZlcnQgYWxsIGNoYXJhY3RlciB2YXJpYWJsZXMgaW50byBmYWN0b3JzOg0KYGBge3J9DQpzcHIgJT4lIA0KICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIGFzLmZhY3RvcikpDQpgYGANCg0KIyMgUm93LXdpc2Ugb3BlcmF0aW9ucw0KQmFjayB0byBvdXIgcXVlc3Rpb25uYWlyZS4gRm9yIGVhY2ggcGFydGljaXBhbnQsIHdlJ2QgbGlrZSB0byBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2Ugb2YgdGhlIGNvbHVtbnMgdGhhdCBzdGFydCB3aXRoICJhbmltYWxzIiB0byBnZXQgb25lIG1lYXN1cmUgb2YgdGhlaXIgaW50ZXJlc3QgaW4gYW5pbWFscy4gV2UgbWlnaHQgdHJ5IHNvbWV0aGluZyBsaWtlIHRoaXM6ICANCmBgYHtyfQ0KYW5pbWFsX3N1cnZleSAlPiUgDQogIHN1bW1hcmlzZShhY3Jvc3Moc3RhcnRzX3dpdGgoImFuaW1hbHMiKSwgbWVhbikpDQpgYGANCg0KQXMgeW91IGNhbiBzZWUsIFIgZ2l2ZXMgdXMgdGhlIGF2ZXJhZ2VzIGZvciBlYWNoIG9mIHRoZSBjb2x1bW5zLCBhY3Jvc3MgYWxsIHBhcnRpY2lwYW50cyAtIG5vdCB3aGF0IHdlJ3JlIGxvb2tpbmcgZm9yISBUaGUgcmVhc29uIGZvciB0aGlzIGlzIHRoYXQgUiBpcyBiZXN0IGF0IGNvbXB1dGluZyBvdmVyIGNvbHVtbnMuIFRvIHNvbHZlIG91ciBwcm9ibGVtLCB3ZSBuZWVkIHRvIGFkZCBgcm93d2lzZSgpYC4gVGhpcyBpcyBzaW1pbGFyIHRvIHRoZSBncm91cGluZyBpZGVhIChgZ3JvdXBfYnkoKWApIHdlIGNhbWUgYWNyb3NzIGEgbGl0dGxlIGVhcmxpZXIuIEhlcmUsIHdlJ3JlIHRlbGxpbmcgUiB0byB0cmVhdCBldmVyeSByb3cgYXMgYSBzaW5nbGUgImdyb3VwIi4gV2UgY2FuIHRoZW4gdXNlIGBjX2Fjcm9zcygpYCwgd2hpY2ggaXMgdGhlIHZlcnNpb24gb2YgYGFjcm9zc2AgdGhhdCB3b3JrcyB3aXRoIHJvdy13aXNlIG9wZXJhdGlvbnMuIFdlIGFsc28gbmVlZCBgbXV0YXRlKClgIGJlY2F1c2Ugd2Ugd2FudCB0byBjcmVhdGUgYSBuZXcgY29sdW1uIHJhdGhlciB0aGFuIHNlZSBhIHN1bW1hcnk6DQpgYGB7cn0NCihhbmltYWxfc3VydmV5IDwtIGFuaW1hbF9zdXJ2ZXkgJT4lIA0KICByb3d3aXNlKCkgJT4lIA0KICBtdXRhdGUoDQogICAgcGV0X2ludGVyZXN0ID0gbWVhbihjX2Fjcm9zcyhzdGFydHNfd2l0aCgiYW5pbWFscyIpKSkNCiAgKSkNCmBgYA0KDQoNCiMgKDExKSBSZXNoYXBpbmcgZGF0YSANCg0KTm93LCBsZXQncyBsb29rIGludG8gaG93IHRvIGNoYW5nZSB0aGUgc2hhcGUgb2YgeW91ciBkYXRhLiBUaGVyZSBhcmUgdHdvIG9wdGlvbnMgaGVyZToNCi0gbWFraW5nIHlvdXIgZGF0YSAibG9uZ2VyIiwgaS5lLiBpbmNyZWFzZSB0aGUgbnVtYmVyIG9mIHJvd3MgYW5kIGRlY3JlYXNlIHRoZSBudW1iZXIgb2YgY29sdW1ucw0KICAtIHVzZWZ1bCBmb3IgdGlkeWluZyBkYXRhLCBlc3BlY2lhbGx5IGNvbW1vbiB3aXRoICJ3aWxkLWNhdWdodCIgZGF0YQ0KICAtIGNvbW1hbmQ6IGBwaXZvdF9sb25nZXIoKWAgKG9sZGVyIGNvbW1hbmQ6IGBnYXRoZXIoKWApDQotIG1ha2luZyB5b3VyIGRhdGEgIndpZGVyIiwgaS5lLiBkZWNyZWFzZSB0aGUgbnVtYmVyIG9mIHJvd3MgYW5kIGluY3JlYXNlIHRoZSBudW1iZXIgb2YgY29sdW1ucw0KICAtIG5vdCAoYXMpIGNvbW1vbiBmb3IgdGlkeWluZyBidXQgZm9yIGNyZWF0aW5nIHN1bW1hcnkgdGFibGVzDQogIC0gY29tbWFuZDogYHBpdm90X3dpZGVyKClgIChvbGRlciBjb21tYW5kOiBgc3ByZWFkKClgKQ0KDQohW1Bpdm90XShpbWcvdGlkeXJfc3ByZWFkX2dhdGhlci5wbmcpe3dpZHRoPTUwJX0NCg0KIyMgcGl2b3RfbG9uZ2VyKCkNCkxldCdzIHRha2UgYW5vdGhlciBsb29rIGF0IHRoZSBxdWVzdGlvbm5haXJlIGRhdGEuVGhlIGZvcm1hdCBpdCBpcyBpbiBpcyB0eXBpY2FsIGZvciBxdWVzdGlvbm5haXJlIGRhdGEgYXMgeW91IHdvdWxkIGV4cG9ydCBpdCBmcm9tIGhvc3Qgc2l0ZXMuIFRoZSBkYXRhIGZyb20gZWFjaCBwZXJzb24gaXMgcmVwcmVzZW50ZWQgb24gb25lIGxpbmUuIFRoaXMgY2FuIGJlIHVzZWZ1bCBmb3Igc29tZSBvcGVyYXRpb25zIChsaWtlIGluIHRoZSBgY2FzZV93aGVuKClgIHdlIHVzZWQgdG8gY3JlYXRlIHRoZSAicHJlZmVyZW5jZSIgdmFyaWFibGUpIGJ1dCBhIHByb2JsZW0gZm9yIG90aGVyIGFwcGxpY2F0aW9ucywgc28gbGV0J3MgdGlkeSB0aGlzIHVzaW5nIGBwaXZvdF9sb25nZXJgLiANClNwZWNpZmljYWxseSwgd2UgaGF2ZSBhbiBpc3N1ZSB3aXRoIHRoZSBkb2cgYW5kIGNhdCByYXRpbmdzIGJlaW5nIGluIHNlcGFyYXRlIGNvbHVtbnMuIEluc3RlYWQsIHdlIHNob3VsZCBoYXZlIGEgY29sdW1uIGNhbGxlZCAiYW5pbWFsIiwgd2hpY2ggY29udGFpbnMgZWl0aGVyICJjYXRzIiBvciAiZG9ncyIsIGFuZCBhbm90aGVyIGNvbHVtbiB0aGF0IGNvbnRhaW5zIHRoZSByYXRpbmcuDQoNClRoZSBiYXNpYyBgcGl2b3RfbG9uZ2VyYCBhcmd1bWVudHMgYXJlOg0KY29sczogd2hpY2ggY29sdW1ucyBzaG91bGQgYmUgcmVzaGFwZWQ/DQpuYW1lc190bzogdGhlIG5hbWUgb2YgdGhlIHZhcmlhYmxlIHRoYXQgdGhlIG9yaWdpbmFsIGNvbHVtbiBuYW1lcyAoImNhdHMiIGFuZCAiZG9ncyIpIHNob3VsZCBiZSBzdG9yZWQgaW4gYXMgdmFsdWVzDQp2YWx1ZXNfdG86IHRoZSBuYW1lIG9mIHRoZSB2YXJpYWJsZSB0aGF0IHRoZSBjb250ZW50cyBvZiB0aGUgdmFyaWFibGVzIHNob3VsZCBiZSBzdG9yZWQgaW4NCmBgYHtyfQ0KYW5pbWFsX3N1cnZleSAlPiUgDQogIHBpdm90X2xvbmdlcihjb2xzID0gYygiY2F0cyIsICJkb2dzIiksDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJhbmltYWwiLA0KICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInJhdGluZyIpDQpgYGANCg0KQW5vdGhlciBleGFtcGxlOiBUaGlzIG5leHQgZmlsZSBjb250YWlucyB0aGUgYXZlcmFnZXMgb2YgYWNjZXB0YWJpbGl0eSBqdWRnZW1lbnRzIGZvciB0aGUgc2VudGVuY2VzIHdlIHVzZWQgaW4gdGhlIGV4cGVyaW1lbnQgKHdlJ2QgZXhwZWN0IHRoYXQgc2VudGVuY2VzIHdoaWNoIGNvbnRhaW4gbWlzbWF0Y2hpbmcgY29sbG9jYXRlcyBzdWNoIGFzICJiYXJraW5nIGNhdCIgYXJlIHJhdGVkIGFzIGxlc3MgYWNjZXB0YWJsZSB0aGFuIHNlbnRlbmNlcyB3aXRoIG1hdGNoaW5nIGNvbGxvY2F0ZXMpLiBMZXQncyByZWFkIGl0IGluOg0KYGBge3J9DQphY2NfanVkZ2Vfb3JpZ2luYWwgPC0gcmVhZF9jc3YoImRhdGEvYWNjZXB0YWJpbGl0eV9qdWRnZW1lbnRzLmNzdiIpDQpgYGANCldlIGNhbiBzZWUgdGhhdCB0aGlzIGRhdGEgaXMgaW4gYSB2ZXJ5IHdpZGUgZm9ybWF0IC0gZWFjaCBjb2x1bW4gbmFtZSBjb250YWlucyBvbmUgb2YgdGhlIHNlbnRlbmNlcywgYW5kIHRoZSBvbmx5IHJvdyBjb25zaXN0cyBvZiB0aGUgYXZlcmFnZSBhY2NlcHRhYmlsaXR5IGp1ZGdlbWVudHMuDQoNCldoYXQgd2UnZCBsaWtlIGluc3RlYWQgaXMgYSBjb2x1bW4gdGhhdCBjb250YWlucyBhbGwgdGhlIHNlbnRlbmNlcyBhbmQgYW5vdGhlciBjb2x1bW4gdGhhdCBjb250YWlucyBhbGwgdGhlIGF2ZXJhZ2VzLg0KYGBge3J9DQooYWNjX2p1ZGdlIDwtIGFjY19qdWRnZV9vcmlnaW5hbCAlPiUgDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gZXZlcnl0aGluZygpLCAjIHdlIHdhbnQgdG8gcmVzaGFwZSB0aGUgZW50aXJlIGRhdGEsIHNvIGFsbCB2YXJpYWJsZXMgYXJlIGNvbmNlcm5lZA0KICAgIG5hbWVzX3RvID0gInNlbnRlbmNlIiwNCiAgICB2YWx1ZXNfdG8gPSAicmF0aW5nIg0KICApKQ0KYGBgDQoNCiMjIHBpdm90X3dpZGVyKCkNCldoaWxlIGBwaXZvdF9sb25nZXIoKWAgaXMgb2Z0ZW4gdXNlZCB0byB0aWR5IGRhdGEsIGl0cyBvcHBvc2l0ZSBgcGl2b3Rfd2lkZXIoKWAgaXMgbW9yZSBjb21tb24gd2hlbiBjcmVhdGluZyBhbmQgcmVmb3JtYXR0aW5nIHN1bW1hcnkgdGFibGVzLiBGb3IgZXhhbXBsZSwgbGV0J3MgZmlyc3QgY291bnQgaG93IG9mdGVuICJjYXQiIGFuZCAiZG9nIiBvY2N1ciB3aXRoIGVhY2ggY29sbG9jYXRlOg0KYGBge3J9DQphbmltYWxfY29ycHVzICU+JSANCiAgZ3JvdXBfYnkoYW5pbWFsLCBjb2xsKSAlPiUgDQogIGNvdW50KCkgJT4lIA0KICB1bmdyb3VwKCkgDQpgYGANCg0KTG9va3MgbGlrZSBpdCdzIHdvcmtpbmchIEhvd2V2ZXIsIHdlJ2QgbGlrZSB0byByZXNoYXBlIHRoaXMgZGF0YSBpbnRvIGEgbW9yZSB0eXBpY2FsIGNvbnRpbmdlbmN5IHRhYmxlLWxpa2UgZm9ybWF0LCB3aXRoICJjYXQiIGFuZCAiZG9nIiBhcyByb3cgbGFiZWxzLCBhbmQgdGhlIGNvbGxvY2F0ZXMgYXMgY29sdW1uIGxhYmVscy4gV2UgY2FuIHVzZSBgcGl2b3Rfd2lkZXIoKWAgdG8gYWNoaWV2ZSB0aGlzLiBJdHMgbWFpbiBhcmd1bWVudHMgYXJlOg0KbmFtZXNfZnJvbTogd2hlcmUgc2hvdWxkIHRoZSBuZXcgY29sdW1uIG5hbWVzIGNvbWUgZnJvbT8NCnZhbHVlc19mcm9tOiB3aGVyZSBzaG91bGQgdGhlIGNvcnJlc3BvbmRpbmcgdmFsdWVzIGNvbWUgZnJvbT8NCmBgYHtyfQ0KYW5pbWFsX2NvcnB1cyAlPiUgDQogIGdyb3VwX2J5KGFuaW1hbCwgY29sbCkgJT4lIA0KICBjb3VudCgpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgcGl2b3Rfd2lkZXIoDQogICAgbmFtZXNfZnJvbSA9IGNvbGwsDQogICAgdmFsdWVzX2Zyb20gPSBuDQogICkNCmBgYA0KDQoNCiMgKDEyKSBKb2luaW5nIHNldmVyYWwgZGF0YSBzZXRzDQpMZXQncyBhZGQgdGhlIGFjY2VwdGFiaWxpdHkganVkZ2VtZW50IGRhdGEgdG8gdGhlIFNQUiBkYXRhOiB3ZSB3YW50IHRvICJqb2luIiB0aGUgdHdvIGRhdGFzZXRzICBpbnRvIG9uZS4NClRoZXJlIGFyZSBzZXZlcmFsIGpvaW4gY29tbWFuZHMsIHdoaWNoIGRpZmZlciBpbiBob3cgdGhleSBtYXRjaCB1cCBkYXRhc2V0cyBhbmQgd2hpY2ggY2FzZXMgYXJlIGtlcHQgb3IgZGlzY2FyZGVkLg0KYGBge3J9DQpzcHJfYWNjIDwtIGxlZnRfam9pbihzcHIsIGFjY19qdWRnZSwgYnkgPSAic2VudGVuY2UiKQ0KDQpzcHJfYWNjX3BhcnRpY2lwYW50cyA8LSBsZWZ0X2pvaW4oc3ByX2FjYywgYW5pbWFsX3N1cnZleSwgYnkgPSAicGFydGljaXBhbnQiKQ0KDQphbmltYWxfZGF0YV9jb21wbGV0ZSA8LSBzcHJfYWNjX3BhcnRpY2lwYW50cyAjIG5lZWQgdG8gam9pbiBjb3JwdXMgZnJlcXVlbmN5DQpgYGANCg0KDQotIGxlZnRfam9pbiwgcmlnaHRfam9pbiwgaW5uZXJfam9pbiwgZnVsbF9qb2luDQotIHNlbWlfam9pbiwgYW50aV9qb2luDQotIGJpbmRfcm93cywgYmluZF9jb2xzDQoNCg0KIyAoMTMpIFdyaXRpbmcgZmlsZXMgdG8gZGlzaw0KDQpUbyB3cml0ZSBmaWxlcyB0byBkaXNrLCB5b3UgYWxzbyBuZWVkIHRvIHNlbGVjdCB3aGljaCBzZXBhcmF0b3IgeW91IHdhbnQgdG8gdXNlIC0tIGRvIHlvdSB3YW50IGEgY29tbWEtc2VwYXJhdGVkIGZpbGUgb3IgYSB0YWItc2VwYXJhdGVkIGZpbGU/IEluIHRoZW9yeSwgdGhpcyBzaG91bGRuJ3QgbWF0dGVyIG11Y2gsIHlvdSBqdXN0IGhhdmUgdG8ga25vdyB3aGljaCB5b3UgcGljay4gDQoNCkNTVnMgYXJlIG9mdGVuIHNhdmVkIGFzIC5jc3YgKHRoaXMgaXMgYWxzbyBvcGVuYWJsZSBpbiBFeGNlbCkgYW5kIFRTVnMgYXJlIG9mdGVuIHNhdmVkIGFzIC50eHQNCg0KWW91IGNhbiBzYXZlIENTVnMgYW5kIFRTVnMgbGlrZSBzbzoNCmBgYHtyfQ0KIyB3cml0ZV90c3Yoc3ByLCAic3ByX2RmLnR4dCIpDQoNCiMgd3JpdGVfY3N2KHNwciwgInNwcl9kZi5jc3YiKQ0KYGBgDQpTeW50YXg6IHdyaXRlX3RzdihkZm5hbWUsICJmaWxlbmFtZS5leHQiKQ0KDQpSZW1lbWJlciwgc29tZXRoaW5nIGxpa2UgZGF0YSB0eXBlIGluIFIgKGV4OiBjaGFyYWN0ZXIgb3IgZmFjdG9yKSBjYW4ndCBiZSBzYXZlZCBpbnRvIG9uZSBvZiB0aGVzZSBmaWxlcyAtLSBpdCBqdXN0IHNhdmVzIHRoZSB2YWx1ZXMgc2VwYXJhdGVkIGJ5IHRoZSBzZXBhcmF0b3IgKGFuZCB0aGUgY29sdW1uIG5hbWVzKS4gU28geW91J2xsIHN0aWxsIGhhdmUgdG8gZG8gdGhlIGNvbnZlcnNpb25zIG5leHQgdGltZSB5b3UgcmVhZCB0aGUgZmlsZSBpbnRvIFIuIA0KDQoNCiMgKDE0KSBJbnRybyB0byBnZ3Bsb3QgYmFzaWNzDQotIHdvcmtpbmcgd2l0aCBzdWJzZXRzIGJ5IGRpcmVjdGx5IHBpcGluZyB0aGVtIGludG8gYSBwbG90dGluZy9tb2RlbCBjYWxsDQoNCiFbZ2dwbG90Ml0oaW1nL2dncGxvdDJfZXhwbG9yYXRvcnkucG5nKXt3aWR0aD01MCV9DQojIyMgVGhlIGxheWVyZWQgZ3JhbW1hciBvZiBncmFwaGljcw0KZ2dwbG90IGZvbGxvd3MgdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MuIEl0IHN0YXJ0cyB3aXRoIGEgYmxhbmsgZ3JhcGggdGhhdCBpcyBmZWQgYSBzcGVjaWZpYyBkYXRhc2V0LCBpLmUuIGdncGxvdChkYXRhPURBVEFTRVQpLiBBdCB0aGlzIHBvaW50LCB0aGUgZ3JhcGggZG9lc24ndCBoYXZlIGFueSBpZGVhIG9mIHdoaWNoIG9mIHRoZSBtYW55IHZhcmlhYmxlcyBpbiB0aGUgZGF0YXNldCB5b3UnZCBsaWtlIHRvIHVzZSwgd2hpY2ggd2lsbCBnbyBvbiB0aGUgeC0gb3IgeS0gYXhpcyBvciB3aGF0IHR5cGUgb2YgZ3JhcGggeW91J3JlIHRyeWluZyB0byBwcm9kdWNlLg0KDQpZb3UgY2FuIHNlZSwgYXQgdGhpcyBzdGFnZSB3ZSBoYXZlLCB3ZWxsLi4uIG5vdGhpbmchDQpgYGB7cn0NCmdncGxvdChkYXRhID0gYW5pbWFsX2RhdGFfY29tcGxldGUpDQpgYGANCg0KVGhlbiwgeW91IGFkZCBsYXllcnMgdG8gdGhpcyBncmFwaC4gVGhlc2UgbGF5ZXJzIGluY2x1ZGUgYW4gKmFlc3RoZXRpYyBtYXBwaW5nKiB3aGljaCBkZWZpbmVzIGhvdyBkYXRhIGlzIG1hdGNoZWQgdG8gdGhlIGdyYXBoaWNzIChlLmcuIHdoaWNoIHZhcmlhYmxlIGlzIHNob3duIG9uIHRoZSB4LWF4aXMsIHdoaWNoIG9uZSBpcyBzaG93biBvbiB0aGUgeS1heGlzKS4NClNpbWlsYXJseSwgeW91IGNhbiBjaGFuZ2UgdGhlIHNoYXBlLCBzaXplLCBhbmQgY29sb3Igb2Ygc29tZSBvciBhbGwgcG9pbnRzL2xpbmVzL2JhcnMuIA0KDQpnZ3Bsb3QgdXNlcyArIHRvIGNvbnRpbnVlIGEgbGluZSBvZiBjb2RlOyB0aGlzIGhhcyB0byBjb21lIGF0IHRoZSBFTkQgb2YgdGhlIHByZXZpb3VzIGxpbmUsIG5vdCBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBuZXh0IGxpbmUuIFRoZXNlIGxpbmUgYnJlYWtzIGFyZSBvcHRpb25hbCwgYnV0IG1ha2UgaXQgYSBsb3QgZWFzaWVyIHRvIHJlYWQgYW5kIHVuZGVyc3RhbmQgdGhlIGNvZGUuDQoNCldoZW4gd2UgdGVsbCBnZ3Bsb3QgaG93IHdlIHdhbnQgdGhlIGFlc3RoZXRpY3MgbWFwcGVkLCBpdCBjYW4gYWxyZWFkeSBzdGFydCB0byBzZXQgdXAgdGhlIGF4ZXMuIEhvd2V2ZXIsIGl0IGRvZXNuJ3QgeWV0IGtub3cgd2hhdCB0eXBlIG9mIGdyYXBoIHdlJ3JlIGJ1aWxkaW5nOg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGFuaW1hbF9kYXRhX2NvbXBsZXRlKSArIA0KICBhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCB5ID0gYmlsbF9kZXB0aF9tbSkgDQpgYGANCg0KRmluYWxseSwgd2UgYWRkIGEgKmdlb20qIHdoaWNoIGRlZmluZXMgdGhlIHR5cGUgb2YgcGxvdCB3ZSdyZSBtYWtpbmcuIFRoaXMgbWF5IGJlIGEgYmFyIGNoYXJ0LCBhIGxpbmUgZ3JhcGgsIGEgc2NhdHRlcnBsb3QsIG9yIG1hbnkgbW9yZSBvcHRpb25zIHdlJ2xsIGV4cGxvcmUgYmVsb3chDQoNCkFzIGEgZmlyc3QgZXhhbXBsZSwgbGV0J3MganVzdCBwdXQgYSBwb2ludCBvciBkb3Qgb24gZWFjaCBkYXRhIHBvaW50IHVzaW5nIHRoZSBheGVzIGFib3ZlOg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGFuaW1hbF9kYXRhX2NvbXBsZXRlKSArDQogIGFlcyh4ID0gUlQsIHkgPSByYXRpbmcpICsNCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KIyMjIE90aGVyIGdlb21zDQoNCiMjIyMgRm9yIG9uZSB2YXJpYWJsZQ0KDQpMZXQncyBsb29rIGF0IHRoZSBudW1iZXIgb2YgcGVuZ3VpbnMgcGVyIHNwZWNpZXMuIEEgYmFyIHBsb3QgaXMgdXNlZnVsIGZvciB0aGlzLiBUaGUgYGFlcygpYCBvbmx5IG5lZWRzIGFuIGFyZ3VtZW50IGZvciB0aGUgeC1heGlzLCBpLmUuIHRoZSBjYXRlZ29yeSBpdCBzaG91bGQgdmlzdWFsaXNlLiBSIHRoZW4gaGVscGZ1bGx5IGNvdW50cyBob3cgbWFueSBkYXRhIHBvaW50cyB0aGVyZSBhcmUgZm9yIGVhY2ggY2F0ZWdvcnksIGkuZS4gaG93IG1hbnkgcGVuZ3VpbnMgcGVyIHNwZWNpZXMuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBhbmltYWxfZGF0YV9jb21wbGV0ZSkgKw0KICBhZXMoeCA9IGVkdWNhdGlvbikgKw0KICBnZW9tX2JhcigpDQpgYGANCg0KVG8gc2hvdyB0aGUgZGlzdHJpYnV0aW9uIG9mIG9uZSBjb250aW51b3VzIHZhcmlhYmxlLCBhIGhpc3RvZ3JhbSBpcyB1c2VmdWwuIEhpc3RvZ3JhbXMgdGFrZSBhIGNvbnRpbnVvdXMgdmFyaWFibGUgYW5kIGRpdmlkZSBpdCBpbnRvICpiaW5zKi4gVGhlc2UgYmlucyBkaXZpZGUgdGhlIGRhdGEgaW50byBlcXVhbGx5IHNpemVkIGNvbnRhaW5lcnMgb3IgcmFuZ2VzLCBhbmQgdGhlbiBzaG93IGhvdyBtYW55IGRhdGEgcG9pbnRzIGZhbGwgaW5zaWRlIG9mIHRoZXNlIHJhbmdlcy4gVGhpcyBzaG93cyB5b3UgaWYgdGhlIGRhdGEgY29tZXMgZnJvbSBhIHNtb290aCByYW5nZSBvciBpZiBtb3JlIGFyZSBjb21pbmcgZnJvbSBhIGNlcnRhaW4gcGFydCBvZiB0aGUgZGlzdHJpYnV0aW9uLiANCg0KTGlrZSBiYXIgcGxvdHMsIHlvdSBvbmx5IG5lZWQgdG8gc3BlY2lmeSBvbmUgdmFyaWFibGUgYW5kIFIgZG9lcyB0aGUgY291bnRpbmcgZm9yIHlvdS4NCmBgYHtyIGhpc3RvZ3JhbX0NCmdncGxvdChkYXRhID0gYW5pbWFsX2RhdGFfY29tcGxldGUpICsNCiAgYWVzKHggPSBSVCkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpDQpgYGANCg0KWW91IGFsc28gc2VlIHRoYXQgUiB0aHJvd3MgYSB3YXJuaW5nIGFib3V0IHRoZSBiaW4gc2l6ZS4gQnkgZGVmYXVsdCwgUiB3aWxsIHRyeSB0byBwaWNrIGEgcmVhc29uYWJsZSBhbW91bnQgb2YgYmlucyB0byBjaGVjayB5b3VyIGRhdGEgb24uIEhvd2V2ZXIsIHlvdSBjYW4gY2hhbmdlIHRoaXMgYW5kIHNlZSBob3cgaXQgY2hhbmdlcyB5b3VyIGlkZWEgb2YgdGhlIGRpc3RyaWJ1dGlvbi4gQ2hhbmdlIHRoaXMgd2l0aCB0aGUgYXJndW1lbnQgYGJpbnM9YCBpbnNpZGUgb2YgdGhlIGdlb21faGlzdG9ncmFtKCkNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGFuaW1hbF9kYXRhX2NvbXBsZXRlKSArDQogIGFlcyh4ID0gUlQpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDUwKQ0KYGBgDQoNCkRlbnNpdHkgcGxvdHMgYXJlIGEgdmFyaWF0aW9uIG9mIGhpc3RvZ3JhbXMgd2hlcmUgYSBzbW9vdGggbGluZSBpbnN0ZWFkIG9mIHNlcGFyYXRlIGJhcnMgaXMgZHJhd24uIFRoaXMgY2FuIGJlIHVzZWZ1bCBmb3IgYSBsYXJnZXIgbnVtYmVyIG9mIGRhdGEgcG9pbnRzIGJ1dCBjYW4gYWxzbyBoaWRlIHNvbWUgb2JzZXJ2YXRpb25zLCBzbyBmb3IgdGhpcyBkYXRhLCB3b3VsZG4ndCBiZSBhZHZpc2FibGUuDQpgYGB7ciBkZW5zaXR5IHBsb3R9DQpnZ3Bsb3QoZGF0YSA9IGFuaW1hbF9kYXRhX2NvbXBsZXRlKSArDQogIGFlcyh4ID0gUlQpICsNCiAgZ2VvbV9kZW5zaXR5KCkNCmBgYA0KDQoNCiMjIyMgRm9yIHR3byB2YXJpYWJsZXMNCg0KRm9yIHR3byBjb250aW51b3VzIHZhcmlhYmxlcywgd2UgY2FuIHVzZSBhIHNjYXR0ZXJwbG90IHdpdGggYGdlb21fcG9pbnQoKWAgdG8gc2hvdyBvbmUgZG90IHBlciBkYXRhIHBvaW50LCBkZWZpbmVkIGFnYWluc3QgdHdvIGNvbnRpbnVvdXMgYXhlcywgbGlrZSB3ZSBzYXcgaW4gdGhlIGV4YW1wbGUuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBwZW5ndWlucykgKw0KICBhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCB5ID0gYmlsbF9kZXB0aF9tbSkgKw0KICBnZW9tX3BvaW50KCkgDQpgYGANCg0KV2UgY2FuIGFsc28gdXNlIGEgKHNtb290aGVkKSBsaW5lIHRvIHJlcHJlc2VudCB0aGUgc2FtZSBkYXRhOg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zKSArDQogIGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIHkgPSBiaWxsX2RlcHRoX21tKSArDQogIGdlb21fc21vb3RoKCkgDQpgYGANCg0KSGVyZSBjb21lcyB0aGUgY29vbCBwYXJ0IGFib3V0IGdncGxvdC4uIHlvdSBjYW4gYWxzbyBkbyBib3RoIGF0IHRoZSBzYW1lIHRpbWUhIFNpbXBseSBhZGQgYm90aCBnZW9tcyB0byB0aGUgc2FtZSBwbG90Og0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gcGVuZ3VpbnMpICsNCiAgYWVzKHggPSBib2R5X21hc3NfZywgeSA9IGJpbGxfbGVuZ3RoX21tKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKCkNCmBgYA0KDQoNCg0KSW4gdGhlIGJveHBsb3QgYmVsb3csIHRoZSB0aGljayBsaW5lIGluIHRoZSBtaWRkbGUgaXMgdGhlIG1lZGlhbiwgdGhlIGJvdHRvbSBvZiB0aGUgYm94IGlzIHRoZSAxc3QgUXVhcnRpbGUgYW5kIHRoZSBsaW5lcyByZWFjaCB1cCB0byB0aGUgbWluaW11bSBhbmQgbWF4aW11bSB2YWx1ZXMgKHNob3duIGFzIGRvdHMgaWYgdGhleSBhcmUgZXh0cmVtZSwgaS5lLiB0aGV5IGFyZSBvdXRzaWRlIG9mIDEuNXggdGhlIHJhbmdlIGZyb20gMXN0IHRvIDNyZCBxdWFydGlsZTogdGhlIGludGVycXVhcnRpbGUgcmFuZ2UpLg0KDQpJbiB0aGlzIHdheSwgYm94cGxvdHMgc2hvdyB5b3Ugd2hlcmUgdGhlIGRhdGEgaXMgbG9jYXRlZCwgYW5kIGhvdyB0aGlzIGRpZmZlcnMgYmV0d2VlbiBncm91cHMuDQoNCmBgYHtyIGJveHBsb3R9DQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zKSArDQogIGFlcyh4ID0gc3BlY2llcywgeSA9IGJpbGxfZGVwdGhfbW0pICsNCiAgZ2VvbV9ib3hwbG90KCkNCmBgYA0KDQpgYGB7ciB2aW9saW4gcGxvdH0NCmdncGxvdChkYXRhID0gcGVuZ3VpbnMpICsNCiAgYWVzKHggPSBzcGVjaWVzLCB5ID0gYmlsbF9kZXB0aF9tbSkgKw0KICBnZW9tX3Zpb2xpbigpIA0KYGBgDQoNCmBgYHtyIHBsb3R9DQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zKSArDQogIGFlcyh4ID0gc3BlY2llcywgeSA9IGJpbGxfZGVwdGhfbW0pICsNCiAgZ2VvbV92aW9saW4oKSArIA0KICBnZW9tX2JveHBsb3QoKSAjdHJ5IGFkZGluZyBhbHBoYSA9IDAuNQ0KYGBgDQoNCiMjIEFkZGluZyBtb3JlIGluZm9ybWF0aW9uDQoNCiMjIyBGaWxsIHZzLiBjb2xvcg0KDQpnZ3Bsb3QgZGlzdGluZ3Vpc2hlcyBiZXR3ZWVuIGBmaWxsPWAgYW5kIGBjb2xvcj1gLiBCb3RoIGhhdmUgdG8gZG8gd2l0aCBjb2xvcnMsIGJ1dCB0aGUgZGlmZmVyZW5jZSBpcyBwcmV0dHkgaW50dWl0aXZlLiBGaWxsIGlzIHVzZWQgZm9yIGZpbGxpbmcgaW4gbGFyZ2VyIHNwYWNlZCBsaWtlIGVudGlyZSBiYXJzLiBDb2xvciBpcyB1c2VkIGZvciBzbWFsbGVyIHNlY3Rpb25zIGxpa2UgZG90cyBvciBsaW5lcy4gSW4gZ2VuZXJhbCwgaWYgeW91ciBmaWxsL2NvbG9yIGFyZ3VtZW50IGRvZXNuJ3Qgd29yaywgdHJ5IHN3aXRjaGluZyBpdCB0byB0aGUgb3RoZXIgb3B0aW9uIGFuZCBzZWUgaWYgdGhhdCBmaXhlcyBpdCENCg0KIyMjIGNvbG91cg0KTGV0J3MgcmV0dXJuIHRvIG9uZSBvZiB0aGUgZmlyc3QgcGxvdHMgd2UgbWFkZS4gSGVyZSwgd2UgY2FuIGFkZCBhIGNvbG9yIGFyZ3VtZW50IHRvIHRoZSAqZ2VvbSogY2FsbCB0aGF0IGNhbGxzIGEgbm9ybWFsIGNvbG9yICgqaW4gcXVvdGVzKikuIFRoaXMgd2lsbCBtYWtlIGFsbCB0aGUgcG9pbnRzIHRoaXMgY29sb3IuIA0KTm90ZTogaWYgeW91IGtub3cgd2hhdCBoZXggY29kZXMgYXJlIChvciBqdXN0IHVzZSBhIGhleCBjb2RlIGZpbmRlciBvbmxpbmUpLCB5b3UgY2FuIHVzZSBhbGwgc29ydHMgb2YgY3VzdG9tIGNvbG9ycy4NCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBwZW5ndWlucykgKw0KICBhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCB5ID0gYmlsbF9kZXB0aF9tbSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiKSANCmBgYA0KDQpIb3dldmVyLCB3ZSBjYW4gYWxzbyB1c2UgdGhlIGNvbG9yIGFyZ3VtZW50IHRvIGludHJvZHVjZSBhIHRoaXJkIHZhcmlhYmxlIGludG8gdGhlIHBpY3R1cmUuIElmIHdlIGFyZSBjYWxsaW5nIGEgdmFyaWFibGUvY29sdW1uLCB3ZSBkbyAqbm90KiBuZWVkIHF1b3RlcyBBTkQgd2UgbmVlZCB0byBwdXQgaXQgaW4gdGhlICphZXMqIHBhcnQgb2YgdGhlIGNhbGwsIGJlY3Vhc2UgaXQgaXMgYSAtbWFwcGluZy0gbm90IGEgc2ltcGxlIGNvbG9yLg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zKSArDQogIGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIHkgPSBiaWxsX2RlcHRoX21tLCBjb2xvciA9IHNwZWNpZXMpICsNCiAgZ2VvbV9wb2ludCgpIA0KYGBgDQoNCkhlcmUncyB3aGF0IGhhcHBlbnMgaWYgeW91IHRyeSB0byBwdXQgYSBwbGFpbiBjb2xvciBpbnNpZGUgb2YgdGhlIGFlcyBtYXBwaW5nLi4uIFIgZG9lc24ndCBmaW5kIHRoaXMgdmFyaWFibGUsIHNvIGl0IGp1c3QgcmFuZG9tbHkgY3JlYXRlcyBpdC4uDQpgYGB7cn0NCmdncGxvdChkYXRhID0gcGVuZ3VpbnMpICsNCiAgYWVzKHggPSBiaWxsX2xlbmd0aF9tbSwgeSA9IGJpbGxfZGVwdGhfbW0sIGNvbG9yID0gImJsdWUiKSArDQogIGdlb21fcG9pbnQoKSANCmBgYA0KDQpUaGUgc2FtZSBpZGVhIHdvcmtzIHJlZ2FyZGxlc3Mgb2YgdGhlIHR5cGUgb2YgcGxvdCwgYXMgbG9uZyBhcyBpdCBpcyBvbmUgdGhhdCB0YWtlcyBjb2xvciBhbmQgbm90IGZpbGw6DQpgYGB7cn0NCmdncGxvdChkYXRhID0gcGVuZ3VpbnMpICsNCiAgYWVzKHggPSBiaWxsX2xlbmd0aF9tbSwgeSA9IGJpbGxfZGVwdGhfbW0sIGNvbG9yID0gc3BlY2llcykgKw0KICBnZW9tX3Ntb290aCgpDQpgYGANCg0KU28geW91IHNlZSB0aGF0IGFkZGluZyBhIG1hcHBpbmcgdG8gY29sb3IgKHRoYXQgaXMgcmVsYXRlZCB0byBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlKSBhbHdheXMgbWFrZXMgdGhlIGFzc2lnbmVkIGdyb3VwcyB2aXNpYmxlIGluIHRoZSBwbG90LiBJbiBhIHdheSwgaXQncyBsaWtlIGFkZGluZyBhIHRoaXJkIHZhcmlhYmxlIHRvIHRoZSBwbG90Lg0KDQpUaGUgZXhhbXBsZSBhYm92ZSBzaG93ZWQgYSAqY2F0ZWdvcmljYWwvZGlzY3JldGUqIHZhcmlhYmxlIGFwcGxpZWQgdG8gY29sb3IuIEhvd2V2ZXIsIHlvdSBjYW4gYWxzbyBkbyBpdCB3aXRoIGEgKm51bWVyaWMqIHZhcmlhYmxlLCBpbiB3aGljaCBjYXNlIHRoZSBjb2xvcnMgd2lsbCByYW5nZSBmcm9tIGxpZ2h0IHRvIGRhcmsgYmx1ZS4NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zKSArDQogIGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIHkgPSBiaWxsX2RlcHRoX21tLCBjb2xvciA9IGJvZHlfbWFzc19nKSArDQogIGdlb21fcG9pbnQoKSANCmBgYA0KDQojIyMgZmlsbA0KQmFyIGNoYXJ0cyBhbmQgb3RoZXIgY2hhcnRzIHdpdGggcHJldHR5IGxhcmdlIG9wZW4gc3BhY2VzIHVzdWFsbHkgbmVlZCBmaWxsLiBGaWxsIHdvcmtzIHRoZSBzYW1lIGFzIGNvbG9yIGluIHRoYXQgaXQgY2FuIGVpdGhlciBnbyAqaW5zaWRlIGFlcygpIGFzIGEgbWFwcGluZyogb3IgKmluc2lkZSB0aGUgZ2VvbSwgd2l0aCBxdW90ZXMsIGFzIGEgcGxhaW4gY29sb3IqLg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zKSArDQogIGFlcyh4ID0gc3BlY2llcykgKw0KICBnZW9tX2JhcihmaWxsID0gInBpbmsiKQ0KYGBgDQoNCkhlcmUsIHdlIGNhbiB1c2UgdGhlIHZhcmlhYmxlICJzcGVjaWVzIiB0byBjb2xvciBjb2RlIHRoZSBiYXJzIGJ5IHNwZWNpZXMuIFRoaXMgaXMgYSAqbWFwcGluZyB0byBhIHZhcmlhYmxlKiwgYnV0IGl0IGlzIHRoZSBzYW1lIHZhcmlhYmxlIHRoYXQgdGhlIHgtYXhpcyBpcyBzaG93aW5nOg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zKSArDQogIGFlcyh4ID0gc3BlY2llcywgZmlsbCA9IHNwZWNpZXMpICsNCiAgZ2VvbV9iYXIoKQ0KYGBgDQoNCg0KU2ltaWxhcmx5IHRvIGNvbG9yLCB5b3UgY2FuIGFsc28gdXNlIGZpbGwgdG8gYWRkIGFuIGFkZGl0aW9uYWwgdmFyaWFibGUgdG8geW91ciBwbG90LiBUaGlzIHdpbGwgY2hhbmdlIHRoZSB0eXBlIG9mIHRoZSBwbG90IHRvIGEgc3RhY2tlZCBiYXIgY2hhcnQsIHdoaWNoIGNhbiBoYW5kbGUgdGhlIGFkZGl0aW9uYWwgdmFyaWFibGUuDQpgYGB7cn0NCmdncGxvdChkYXRhID0gcGVuZ3VpbnMpICsNCiAgYWVzKHggPSBzcGVjaWVzLCBmaWxsPSBzZXgpICsNCiAgZ2VvbV9iYXIoKQ0KYGBgDQoNCkJhciBwbG90cyBjYW4gYWN0dWFsbHkgYWxzbyB0YWtlIGNvbG9yLCBidXQgdGhpcyBhZmZlY3RzIHRoZSBjb2xvciBvZiB0aGUgbGluZXMgb25seToNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBwZW5ndWlucykgKw0KICBhZXMoeCA9IHNwZWNpZXMsIGNvbG9yPSBzcGVjaWVzKSArDQogIGdlb21fYmFyKCkNCmBgYA0KDQoNCiMgKDE1KSBHZXR0aW5nIGhlbHANCg0KV2UgaG9wZSB0byBoYXZlIHByb3ZpZGVkIHlvdSB3aXRoIGEgbG90IG9mIHRvb2xzIGFuZCBrbm93bGVkZ2UgdG9kYXkgdGhhdCB3aWxsIGhlbHAgeW91IHdvcmsgd2l0aCB5b3VyIGRhdGEhIEhvd2V2ZXIsIGl0IG9idmlvdXNseSBpc24ndCBwb3NzaWJsZSB0byBjb3ZlciBhbGwgdGlkeXZlcnNlIGZ1bmN0aW9ucyB0aGF0IG1pZ2h0IGJlIHVzZWZ1bCBmb3IgeW91ciBvd24gZGF0YSBjbGVhbmluZyBhbmQgYW5hbHlzaXMuIFNpbWlsYXJseSwgdGhpcyBvZnRlbiBpc24ndCBhcyBzbW9vdGggYSBwcm9jZXNzIGFzIHdlJ3ZlIHByZXNlbnRlZCBoZXJlIHRvZGF5LiBTb21ldGltZXMsIHlvdSdyZSBub3Qgc3VyZSB3aGljaCBjb21tYW5kcyB0byB1c2UsIG9yIHdoaWNoIGFyZ3VtZW50cyBhIGZ1bmN0aW9uIGhhcywgb3IgZXZlbiB3aGljaCBmb3JtYXQgeW91ciBkYXRhIGFjdHVhbGx5IG5lZWRzIHRvIGJlIGluISBUaGlzIGNhbiBiZSBhIGZydXN0cmF0aW5nIGV4cGVyaWVuY2UsIHNvIHdlIHdhbnRlZCB0byBzaGFyZSBzb21lIHN1Z2dlc3Rpb25zIGZvciBob3cgdG8gcmUtZnJhbWUgbmVnYXRpdmUgdGhvdWdodHMgYW5kIHR1cm4gdGhlbSBpbnRvIG1vcmUgcG9zaXRpdmUgYW5kIGVuY291cmFnaW5nIG9uZXM6DQoNCiFbSGFkbGV5IFdpY2toYW0gb24gaG93IHRvIGRlYWwgd2l0aCBmcnVzdHJhdGluZyBwcm9ibGVtcywgZnJvbSBoaXMga2V5bm90ZSBhdCB0aGUgUlN0dWRpbyAgY29uZmVyZW5jZSAyMDIxXShpbWcvU2VsZnNvb3RoaW5nUi5qcGcpDQoNCiMjIEZ1bmN0aW9uIGRvY3VtZW50YXRpb25zICYgdmlnbmV0dGVzDQoNCkFzIHdlIHNob3dlZCB5b3UgZWFybGllciwgdGhlIGZ1bmN0aW9uIGRvY3VtZW50YXRpb24gKGFjY2VzcyB2aWEgYD9mdW5jdGlvbmAgb3IgYnkgdHlwaW5nIHRoZSBmdW5jdGlvbiBpbnRvIHRoZSAiSGVscCIgcGFuZWwgb24gdGhlIGJvdHRvbSByaWdodCkgaXMgcmVhbGx5IHVzZWZ1bCwgYnV0IGRvbid0IGV4cGVjdCB0byB1bmRlcnN0YW5kIGV2ZXJ5IHNpbmdsZSBkZXRhaWwgKHRoYXQncyB1c3VhbGx5IG5vdCBuZWNlc3NhcnkgYW55d2F5IGZvciBmaWd1cmluZyBvdXQgaG93IHRvIG1ha2UgaXQgd29yayBvciBpZiB5b3UncmUgbW9zdGx5IGNoZWNraW5nIGhvdyB0byBzcGVsbCBhbiBhcmd1bWVudCwgZXRjLikuIElmIHlvdSdyZSB0cnlpbmcgdG8gbGVhcm4gaG93IHRvIHVzZSBhIG5ldyBmdW5jdGlvbiBvciByZW1pbmQgeW91cnNlbGYgb2YgZnVuY3Rpb25zIHlvdSBoYXZlbid0IHVzZWQgaW4gYSB3aGlsZSBhbmQgYXJlIGZpbmRpbmcgaXQgaGFyZCB0byB3cmFwIHlvdXIgaGVhZCBhcm91bmQgaXQgYnkganVzdCByZWFkaW5nIHRoZSBkb2N1bWVudGF0aW9uLCBzY3JvbGwgYWxsIHRoZSB3YXkgZG93biB0byB0aGUgZXhhbXBsZXMgYW5kIHBsYXkgYXJvdW5kIHdpdGggdGhvc2UuIA0KDQpGb3Igc29tZSBmdW5jdGlvbnMsICoqdmlnbmV0dGVzKiogYXJlIGF2YWlsYWJsZS4gRm9yIGluc3RhbmNlLCBpZiB5b3Ugc2VhcmNoIGZvciBoZWxwIG9uIGVpdGhlciBvZiB0aGUgdHdvIHBpdm90aW5nIGZ1bmN0aW9ucywgeW91J2xsIGJlIGRpcmVjdGVkIHRvIGEgdmlnbmV0dGUgb24gcGl2b3RpbmcuIEVudGVyIGB2aWduZXR0ZSgicGl2b3QiKWAgdG8gYWNjZXNzIGl0LiBJdCdsbCBvcGVuIGluIHRoZSBIZWxwIHdpbmRvdyBhbmQgaXMgbW9yZSBsaWtlIGEgZGV0YWlsZWQgdHV0b3JpYWwgd2l0aCBsb3RzIG9mIGV4YW1wbGUgZGF0YSB0byBoZWxwIHlvdSB1bmRlcnN0YW5kIG1vcmUgY29tcGxleCBmdW5jdGlvbnMuDQoNCkJ5IHRoZSB3YXk6IFdlIGhhdmVuJ3QgaGFkIHRpbWUgdG8gZGlzY3VzcyAqYWxsKiBhcmd1bWVudHMgdGhhdCBlYWNoIG9mIHRoZSBmdW5jdGlvbnMgd2UgbGVhcm5lZCB0b2RheSBjYW4gdGFrZSwgc28gaWYgeW91IHRoaW5rIGEgcGFydGljdWxhciBmdW5jdGlvbiBtaWdodCBhbG1vc3Qgc29sdmUgeW91ciBwcm9ibGVtLCBjaGVjayBpdHMgZG9jdW1lbnRhdGlvbiAtIG1heWJlIHRoZXJlJ3MgYW4gZXh0cmEgYXJndW1lbnQgdGhhdCBkb2VzIGV4YWN0bHkgd2hhdCB5b3UncmUgbG9va2luZyBmb3IhDQoNCg0KIyMgV29ya2luZyB3aXRoIEdvb2dsZSByZXN1bHRzDQoNCklmIHlvdSBlbmNvdW50ZXIgYSBzcGVjaWZpYyBwcm9ibGVtIGJ1dCBhcmVuJ3Qgc3VyZSB3aGljaCBmdW5jdGlvbiB0byB1c2UgKG9yIGlmIHlvdSdyZSB0cnlpbmcgdG8gdXNlIGEgY29tbWFuZCB5b3Uga25vdyBpbiBhIHZlcnkgc3BlY2lmaWMgd2F5KSwgaXQncyBvZnRlbiBhIGdvb2QgaWRlYSB0byBzaW1wbHkgZ29vZ2xlIHRoZSBxdWVzdGlvbiAoYW5kIHRoZSBzYW1lIGdvZXMgZm9yIGVycm9yIG1lc3NhZ2VzIC0gYWZ0ZXIgeW91IGRvdWJsZS1jaGVja2VkIHNwZWxsaW5nLCBjYXBpdGFsaXNhdGlvbiwgY29tbWFzLCBtaXNzaW5nIGJyYWNrZXRzLi4uKS4gUiBnZW5lcmFsbHkgaGFzIGEgbGFyZ2UgYW5kIGhlbHBmdWwgY29tbXVuaXR5LCBzbyB5b3UnbGwgbGlrZWx5IGZpbmQgYW5zd2VycyBvbiBzaXRlcyBzdWNoIGFzIFN0YWNrIE92ZXJmbG93IG9yLCBpZiB5b3UncmUgbHVja3ksIGV2ZW4gZW50aXJlIGJsb2dwb3N0cyB3cml0dGVuIG9uIGEgcmVsZXZhbnQgaXNzdWUuDQoNCg0KIyMgUmVzb3VyY2VzIGFuZCBjaGVhdCBzaGVldHMNCg0KLSBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkLmNvLm56L2luZGV4Lmh0bWwpDQotIFtDb2xsZWN0aW9uIG9mIGNoZWF0IHNoZWV0c10oaHR0cHM6Ly9yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKQ0KDQoNCiMgKDE2KSBXb3JrZmxvd3MvY29kZSBjb252ZW50aW9ucw0KDQojIyBQYWNrYWdlcw0KV2hlbiBpbnN0YWxsaW5nIG5ldyBwYWNrYWdlcywgZG9uJ3QgcHV0IHRoZSBjb2RlIGZvciB0aGF0IGluIHlvdXIgc2NyaXB0IC0geW91IGRvbid0IHdhbnQgcGFja2FnZXMgdG8gYmUgaW5zdGFsbGVkIGV2ZXJ5IHRpbWUgeW91IHJ1biBhIHNjcmlwdCB3aGVuIHRoZXkncmUgYWxyZWFkeSB0aGVyZS4NCkFsd2F5cyBsaXN0IGFuZCBsb2FkIGFsbCBwYWNrYWdlcyBhdCB0aGUgdG9wIG9mIHlvdXIgc2NyaXB0IQ0KDQojIyBOYW1pbmcgY29udmVudGlvbnMNCkdpdmUgZXhwcmVzc2l2ZSBuYW1lcyB0byB5b3VyIHZhcmlhYmxlcyBhbmQgZGF0YWZyYW1lcy90aWJibGVzLiBEb24ndCBqdXN0IGNhbGwgZXZlcnl0aGluZyAiZGF0YSIsICJkZiIsIG9yICJ2YXJpYWJsZTEiLCAidmFyMSIsIGV0Yy4sIGJ1dCB1c2UgbmFtZXMgdGhhdCBpbW1lZGlhdGVseSB0ZWxsIHlvdSB3aGF0IHRoZSB2YXJpYWJsZSBjb250YWlucy4NClVzZSBjb25zaXN0ZW50IG5hbWluZyBjb252ZW50aW9ucyBmb3IgdmFyaWFibGVzLiBGb3IgZXhhbXBsZSwgaWYgeW91IHVzZSBhbiB1bmRlcnNjb3JlIHRvIHNlcGFyYXRlIHdvcmRzIGluIG9uZSBtdWx0aS13b3JkIHZhcmlhYmxlIG5hbWUsIHVzZSB0aGUgc2FtZSBzeW1ib2wgZXZlcnl3aGVyZS4NClNpbWlsYXJseSwgYmUgY29uc2lzdGVudCB3aXRoIGNhcGl0YWxpc2F0aW9uIC0gdGhlIHJlY29tbWVuZGF0aW9uIGhlcmUgaXMgdG8gdXNlIGxvd2VyY2FzZSBmb3IgYWxsIGRhdGEgYW5kIHZhcmlhYmxlIG5hbWVzIHRvIHJlZHVjZSB0aGUgY2hhbmdlIG9mIG1pc3NwZWxsaW5nIHZhcmlhYmxlcy4NCg0KIyMgTGluZSBicmVha3MNClRocm91Z2hvdXQgdGhlc2UgbWF0ZXJpYWxzLCB3ZSd2ZSBiZWVuIHVzaW5nIGxpbmUgYnJlYWtzIGFmdGVyIHBpcGVzIGFuZCBvZnRlbiBhbHNvIGFmdGVyIGVhY2ggYXJndW1lbnQgb2YgYSBmdW5jdGlvbi4gVGhpcyBpcyBwdXJlbHkgZm9yIHJlYWRhYmlsaXR5J3Mgc2FrZSAtIG9wdGlvbmFsLCBidXQgaGlnaGx5IHJlY29tbWVuZGVkIQ0KV2UndmUgYWxzbyBjb25zaXN0ZW50bHkgYXNzaWduZWQgdG8gdGhlIGRhdGFmcmFtZSAoZS5nLiBzcHIgPC0gc3ByICU+JSBzb21lIGNvbW1hbmRzKSBhdCB0aGUgYmVnaW5uaW5nIG9mIGEgY29kZSBibG9jaywgYWx0aG91Z2ggaXQncyBhbHNvIHBvc3NpYmxlIHRvIGRvIHRoYXQgYXQgdGhlIGVuZCBvZiB0aGUgY29kZSBibG9jay4gDQoNCiMjIEJ1aWxkaW5nIHVwIGNvZGUgYmxvY2tzL2NoYWluaW5nIGNvbW1hbmRzDQpXaGVuIHdvcmtpbmcgb24gYSAobW9yZSkgY29tcGxleCBkYXRhIHdyYW5nbGluZyBwcm9ibGVtLCB5b3UnbGwgb2Z0ZW4gbmVlZCB0byB0YWtlIHNldmVyYWwgc3RlcHMgdG93YXJkcyB5b3VyIGdvYWwsIGkuZS4gdXNlIHNldmVyYWwgY29tbWFuZHMgdG8gYXJyaXZlIGF0IGEgdGlkeSBhbmQgd29ya2FibGUgZGF0YWZyYW1lLiBJZiB5b3UncmUgc3R1Y2sgYXQgdGhpcyBzdGVwLCB0aGluayBhYm91dCB3aGF0IHlvdSB3YW50IHlvdXIgZGF0YSB0byBsb29rIGxpa2UsIHRoZW4gaWRlbnRpZnkgc2luZ2xlIHN0ZXBzIHRvd2FyZHMgdGhhdCBmb3JtLCB0aGVuIHRoaW5rIGFib3V0IGhvdyB0byBleHByZXNzIGVhY2ggb2YgdGhlc2UgaW4gUi4gSXQgb2Z0ZW4gaGVscHMgdG8gZHJhdyBvciB0YWtlIG5vdGVzIG9uIHBhcGVyIHRvIGJldHRlciB0aGluayB0aHJvdWdoIHRoaXMgcHJvY2VzcyENCkluIHRlcm1zIG9mIGFjdHVhbGx5IHdyaXRpbmcgY29kZSwgaWRlYWxseSBhbHNvIGJ1aWxkIGl0IHVwIHN0ZXAgYnkgc3RlcCBhbmQgZG9uJ3Qgc2F2ZSB5b3VyIGNoYW5nZXMgdW50aWwgeW91J3JlIGNlcnRhaW4gdGhleSBhcmUgd2hhdCB5b3Ugd2FudC4gU29tZXRpbWVzLCBpdCBjYW4gYWxzbyBiZSBhIGdvb2QgaWRlYSBub3QgdG8gb3ZlcndyaXRlIHRoZSBvcmlnaW5hbCBkYXRhZnJhbWUgYnV0IHRvIHNhdmUgaXQgdW5kZXIgYSBkaWZmZXJlbnQgbmFtZSwgZS5nLiBmb3IgbWFqb3IgcmVzdHJ1Y3R1cmluZyBzdWNoIGFzIHBpdm90aW5nLg0KDQoNCiMgTWlzc2luZy9xdWVzdGlvbmFibGUgY29tbWFuZHMNCi0gcGFja2FnZTo6Y29tbWFuZA0KLSBuZXN0X2J5ICANCi0gY29hbGVzY2UgIA0KLSBsYWcgYW5kIGxlYWQgIA0KLSBjdW11bGF0aXZlIGFnZ3JlZ2F0ZXMgIA0KLSB0b3BfbigpDQotIHNhbXBsZSgpPw0KDQo=